Initial commit
FossilOrigin-Name: 370c4e8ed3567a4e0f0933ac3ba749973ba041efa3b7d61310a6e5d296e68e21
This commit is contained in:
parent
03adbf588f
commit
244029a0c8
9 changed files with 2243 additions and 0 deletions
25
LICENSE
Normal file
25
LICENSE
Normal file
|
@ -0,0 +1,25 @@
|
|||
Copyright (C) 2023 Daniel Ziltener
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
Makefile
Normal file
23
Makefile
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
# Tangling and weaving
|
||||
|
||||
webdriver.scm webdriver-impl.scm webdriver.html webdriver.egg webdriver.release-info tests/run.scm: webdriver.org
|
||||
emacs --batch \
|
||||
--load "vendor/htmlize.el" \
|
||||
--eval "(setq enable-local-variables :all)" \
|
||||
--eval "(setq org-confirm-babel-evaluate nil)" \
|
||||
--eval "(require 'ob-tangle)" \
|
||||
--eval "(require 'ob-lob)" \
|
||||
--eval "(require 'htmlize)" \
|
||||
--file "${.ALLSRC:[1]}" \
|
||||
-f org-babel-tangle \
|
||||
-f org-html-export-to-html
|
||||
mv "${.ALLSRC:[1]:R}.html" "${.ALLSRC:[1]:R}.html.old"
|
||||
echo "<div class='fossil-doc' data-title='`cat "${.ALLSRC:[1]:R}.html.old" | grep 'class="title"' | sed -e 's/^.*">//' | sed -e 's/<\/h1>//'`'>" > "${.ALLSRC:[1]:R}.html"
|
||||
cat "${.ALLSRC:[1]:R}.html.old" | sed -e '1,13d' | head -n -2 >> "${.ALLSRC:[1]:R}.html"
|
||||
echo "</div>" >> "${.ALLSRC:[1]:R}.html"
|
||||
rm "${.ALLSRC:[1]:R}.html.old"
|
||||
|
||||
# Extension Rules
|
||||
.scm.so:
|
||||
csc5 -R r7rs -X r7rs -sJ ${.IMPSRC}
|
57
tests/run.scm
Normal file
57
tests/run.scm
Normal file
|
@ -0,0 +1,57 @@
|
|||
(import (chicken string))
|
||||
(import r7rs
|
||||
test
|
||||
(chicken base)
|
||||
(chicken string)
|
||||
(chicken process)
|
||||
(chicken gc)
|
||||
srfi-34 ;;Exception Handling
|
||||
srfi-35 ;;Exception Types
|
||||
base64 ;;decoding screenshot data
|
||||
http-client ;;API interaction
|
||||
intarweb ;;Supporting HTTP functionality
|
||||
uri-common ;;Supporting HTTP functionality
|
||||
coops ;;Object system
|
||||
alist-lib ;;Handling alists from JSON objects
|
||||
medea ;;JSON handling
|
||||
)
|
||||
|
||||
;; [[file:../webdriver.org::*Dependencies][Dependencies:5]]
|
||||
(include-relative "../webdriver-impl.scm")
|
||||
;; Dependencies:5 ends here
|
||||
|
||||
|
||||
|
||||
;; #+name: prep-geckodriver-test
|
||||
|
||||
;; [[file:../webdriver.org::prep-geckodriver-test][prep-geckodriver-test]]
|
||||
|
||||
;; prep-geckodriver-test ends here
|
||||
|
||||
|
||||
|
||||
;; #+name: wd-session-test
|
||||
|
||||
;; [[file:../webdriver.org::wd-session-test][wd-session-test]]
|
||||
(test-group "session"
|
||||
(let ((browser (new-WebDriver <Gecko>)))
|
||||
(test "Initial state" #f (slot-value browser 'session-id))
|
||||
(test-assert "Session id check" (string? (begin (initialize-session browser) (slot-value browser 'session-id))))
|
||||
(test-assert "Session id after termination" (eq? #f (begin (terminate-session browser) (slot-value browser 'session-id))))
|
||||
(terminate browser)))
|
||||
;; wd-session-test ends here
|
||||
|
||||
|
||||
|
||||
;; #+name: wd-url-test
|
||||
|
||||
;; [[file:../webdriver.org::wd-url-test][wd-url-test]]
|
||||
(test-group "url"
|
||||
(let ((browser (new-WebDriver <Gecko>)))
|
||||
(test "Initial state" #f (slot-value browser 'session-id))
|
||||
(test "Navigating to the first website" "http://info.cern.ch/hypertext/WWW/TheProject.html"
|
||||
(begin (initialize-session browser)
|
||||
(set-url browser "http://info.cern.ch/hypertext/WWW/TheProject.html")
|
||||
(url browser)))
|
||||
(terminate browser)))
|
||||
;; wd-url-test ends here
|
497
webdriver-impl.scm
Normal file
497
webdriver-impl.scm
Normal file
|
@ -0,0 +1,497 @@
|
|||
;; [[file:webdriver.org::*Dependencies][Dependencies:4]]
|
||||
(import r7rs
|
||||
(chicken base)
|
||||
(chicken string)
|
||||
(chicken process)
|
||||
(chicken gc)
|
||||
srfi-34 ;;Exception Handling
|
||||
srfi-35 ;;Exception Types
|
||||
base64 ;;decoding screenshot data
|
||||
http-client ;;API interaction
|
||||
intarweb ;;Supporting HTTP functionality
|
||||
uri-common ;;Supporting HTTP functionality
|
||||
coops ;;Object system
|
||||
alist-lib ;;Handling alists from JSON objects
|
||||
medea ;;JSON handling
|
||||
)
|
||||
;; Dependencies:4 ends here
|
||||
|
||||
|
||||
|
||||
;; * Error Conditions
|
||||
|
||||
;; #+name: wd-exception
|
||||
|
||||
;; [[file:webdriver.org::wd-exception][wd-exception]]
|
||||
(define-condition-type &wd-exception &error wd-exception?
|
||||
(stacktrace wd-stacktrace)
|
||||
(data wd-data))
|
||||
;; wd-exception ends here
|
||||
|
||||
|
||||
|
||||
;; #+name: conditions
|
||||
|
||||
;; [[file:webdriver.org::conditions][conditions]]
|
||||
(define-condition-type &detached-shadow-root &wd-exception detached-shadow-root?)
|
||||
(define-condition-type &element-click-intercepted &wd-exception element-click-intercepted?)
|
||||
(define-condition-type &element-not-interactable &wd-exception element-not-interactable?)
|
||||
(define-condition-type &insecure-certificate &wd-exception insecure-certificate?)
|
||||
(define-condition-type &invalid-argument &wd-exception invalid-argument?)
|
||||
(define-condition-type &invalid-cookie-domain &wd-exception invalid-cookie-domain?)
|
||||
(define-condition-type &invalid-element-state &wd-exception invalid-element-state?)
|
||||
(define-condition-type &invalid-selector &wd-exception invalid-selector?)
|
||||
(define-condition-type &invalid-session-id &wd-exception invalid-session-id?)
|
||||
(define-condition-type &javascript-error &wd-exception javascript-error?)
|
||||
(define-condition-type &move-target-out-of-bounds &wd-exception move-target-out-of-bounds?)
|
||||
(define-condition-type &no-such-alert &wd-exception no-such-alert?)
|
||||
(define-condition-type &no-such-cookie &wd-exception no-such-cookie?)
|
||||
(define-condition-type &no-such-element &wd-exception no-such-element?)
|
||||
(define-condition-type &no-such-frame &wd-exception no-such-frame?)
|
||||
(define-condition-type &no-such-shadow-root &wd-exception no-such-shadow-root?)
|
||||
(define-condition-type &no-such-window &wd-exception no-such-window?)
|
||||
(define-condition-type &script-timeout &wd-exception script-timeout?)
|
||||
(define-condition-type &session-not-created &wd-exception session-not-created?)
|
||||
(define-condition-type &stale-element-reference &wd-exception stale-element-reference?)
|
||||
(define-condition-type &timeout &wd-exception timeout?)
|
||||
(define-condition-type &unable-to-capture-screen &wd-exception unable-to-capture-screen?)
|
||||
(define-condition-type &unable-to-set-cookie &wd-exception unable-to-set-cookie?)
|
||||
(define-condition-type &unexpected-alert-open &wd-exception unexpected-alert-open?)
|
||||
(define-condition-type &unknown-command &wd-exception unknown-command?)
|
||||
(define-condition-type &unknown-error &wd-exception unknown-error?)
|
||||
(define-condition-type &unknown-method &wd-exception unknown-method?)
|
||||
(define-condition-type &unsupported-operation &wd-exception unsupported-operation?)
|
||||
|
||||
(define (wd-throw data)
|
||||
(case (alist-ref data 'error)
|
||||
(("detached shadow root") (raise (make-condition &detached-shadow-root (alist-ref data 'stacktrace) data)))
|
||||
(("element click intercepted") (raise (make-condition &element-click-intercepted (alist-ref data 'stacktrace) data)))
|
||||
(("element not interactable") (raise (make-condition &element-not-interactable (alist-ref data 'stacktrace) data)))
|
||||
(("insecure certificate") (raise (make-condition &insecure-certificate (alist-ref data 'stacktrace) data)))
|
||||
(("invalid argument") (raise (make-condition &invalid-argument (alist-ref data 'stacktrace) data)))
|
||||
(("invalid cookie domain") (raise (make-condition &invalid-cookie-domain (alist-ref data 'stacktrace) data)))
|
||||
(("invalid element state") (raise (make-condition &invalid-element-state (alist-ref data 'stacktrace) data)))
|
||||
(("invalid selector") (raise (make-condition &invalid-selector (alist-ref data 'stacktrace) data)))
|
||||
(("invalid session id") (raise (make-condition &invalid-session-id (alist-ref data 'stacktrace) data)))
|
||||
(("javascript error") (raise (make-condition &javascript-error (alist-ref data 'stacktrace) data)))
|
||||
(("move target out of bounds") (raise (make-condition &move-target-out-of-bounds (alist-ref data 'stacktrace) data)))
|
||||
(("no such alert") (raise (make-condition &no-such-alert (alist-ref data 'stacktrace) data)))
|
||||
(("no such cookie") (raise (make-condition &no-such-cookie (alist-ref data 'stacktrace) data)))
|
||||
(("no such element") (raise (make-condition &no-such-element (alist-ref data 'stacktrace) data)))
|
||||
(("no such frame") (raise (make-condition &no-such-frame (alist-ref data 'stacktrace) data)))
|
||||
(("no such shadow root") (raise (make-condition &no-such-shadow-root (alist-ref data 'stacktrace) data)))
|
||||
(("no such window") (raise (make-condition &no-such-window (alist-ref data 'stacktrace) data)))
|
||||
(("script timeout") (raise (make-condition &script-timeout (alist-ref data 'stacktrace) data)))
|
||||
(("session not created") (raise (make-condition &session-not-created (alist-ref data 'stacktrace) data)))
|
||||
(("stale element reference") (raise (make-condition &stale-element-reference (alist-ref data 'stacktrace) data)))
|
||||
(("timeout") (raise (make-condition &timeout (alist-ref data 'stacktrace) data)))
|
||||
(("unable to capture screen") (raise (make-condition &unable-to-capture-screen (alist-ref data 'stacktrace) data)))
|
||||
(("unable to set cookie") (raise (make-condition &unable-to-set-cookie (alist-ref data 'stacktrace) data)))
|
||||
(("unexpected alert open") (raise (make-condition &unexpected-alert-open (alist-ref data 'stacktrace) data)))
|
||||
(("unknown command") (raise (make-condition &unknown-command (alist-ref data 'stacktrace) data)))
|
||||
(("unknown error") (raise (make-condition &unknown-error (alist-ref data 'stacktrace) data)))
|
||||
(("unknown method") (raise (make-condition &unknown-method (alist-ref data 'stacktrace) data)))
|
||||
(("unsupported operation") (raise (make-condition &unsupported-operation (alist-ref data 'stacktrace) data)))
|
||||
(else (raise (make-condition &wd-exception (alist-ref data 'stacktrace) data)))
|
||||
)
|
||||
)
|
||||
;; conditions ends here
|
||||
|
||||
|
||||
|
||||
;; * WebDriver
|
||||
|
||||
;; The core element of the library is the ~<WebDriver>~ class and its subclasses. The class has the following fields:
|
||||
|
||||
;; #+name: webdriver-class
|
||||
|
||||
;; [[file:webdriver.org::webdriver-class][webdriver-class]]
|
||||
(define-class <WebDriver> ()
|
||||
((browser #f)
|
||||
(active? #f)
|
||||
(browser-pid #f)
|
||||
(server #f)
|
||||
(port #f)
|
||||
(session-id #f)
|
||||
(prefs #f)
|
||||
(capabilities #f)))
|
||||
;; webdriver-class ends here
|
||||
|
||||
|
||||
|
||||
;; The parent class provides a handful of methods, but does not implement all of them; some are the sole responsibility of the subclass. The ~launch~ method, on the other hand, bears shared responsibility. It sets a finalizer to ensure termination of the web driver process in case the class is disposed of with a still-open driver.
|
||||
|
||||
;; #+name: webdriver-basics
|
||||
|
||||
;; [[file:webdriver.org::webdriver-basics][webdriver-basics]]
|
||||
(define-method (launch #:after (instance <WebDriver>) options)
|
||||
(set-finalizer! instance (lambda (obj)
|
||||
(when (slot-value instance 'active?)
|
||||
(terminate instance)))))
|
||||
|
||||
(define-method (terminate (instance <WebDriver>))
|
||||
(terminate-session instance)
|
||||
(process-signal (slot-value instance 'browser-pid))
|
||||
(set! (slot-value instance 'browser-pid) #f)
|
||||
(set! (slot-value instance 'active?) #f))
|
||||
|
||||
(define-method (construct-capabilities (instance <WebDriver>) #!optional caps)
|
||||
(raise 'subclass-responsibility))
|
||||
|
||||
(define-method (postprocess-result (instance <WebDriver>) result)
|
||||
result)
|
||||
;; webdriver-basics ends here
|
||||
|
||||
|
||||
|
||||
;; Main initialization is done by calling the ~new-WebDriver~ procedure with the respective class name and optionally an alist of options.
|
||||
|
||||
;; #+name: webdriver-init
|
||||
|
||||
;; [[file:webdriver.org::webdriver-init][webdriver-init]]
|
||||
(define (new-WebDriver browser #!optional options)
|
||||
(let ((instance (make browser)))
|
||||
(launch instance options)
|
||||
(sleep 1)
|
||||
instance))
|
||||
;; webdriver-init ends here
|
||||
|
||||
|
||||
|
||||
;; ** Geckodriver
|
||||
|
||||
;; The Geckodriver is used to control Firefox.
|
||||
|
||||
;; #+name: geckodriver-basic
|
||||
|
||||
;; [[file:webdriver.org::geckodriver-basic][geckodriver-basic]]
|
||||
(define-class <Gecko> (<WebDriver>)
|
||||
((browser #:firefox)
|
||||
(server "127.0.0.1")
|
||||
(port 4444)))
|
||||
|
||||
(define-method (launch (instance <Gecko>) options)
|
||||
(let ((pid (process-run "geckodriver > /dev/null 2>&1")))
|
||||
(set! (slot-value instance 'browser-pid) pid)
|
||||
(set! (slot-value instance 'active?) #t)
|
||||
(set! (slot-value instance 'capabilities) options)))
|
||||
;; geckodriver-basic ends here
|
||||
|
||||
|
||||
|
||||
;; The capabilities object for Geckodriver is of the form ={"capabilities": {...}}=.
|
||||
;; For more information on capabilities, see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities.
|
||||
|
||||
;; #+name: geckodriver-capabilities
|
||||
|
||||
;; [[file:webdriver.org::geckodriver-capabilities][geckodriver-capabilities]]
|
||||
(define-method (construct-capabilities (instance <Gecko>))
|
||||
(let ((caps (or (slot-value instance 'capabilities) (list))))
|
||||
`((capabilities . ,caps))))
|
||||
;; geckodriver-capabilities ends here
|
||||
|
||||
|
||||
|
||||
;; Sometimes, Geckodriver returns the results of a command in a JSON object with the sole key ="value"=. We have to correct that before returning the data to the user.
|
||||
|
||||
;; #+name: geckodriver-postprocess
|
||||
|
||||
;; [[file:webdriver.org::geckodriver-postprocess][geckodriver-postprocess]]
|
||||
(define-method (postprocess-result (instance <Gecko>) result)
|
||||
(alist-ref/default result 'value result))
|
||||
;; geckodriver-postprocess ends here
|
||||
|
||||
|
||||
|
||||
;; * WebDriver API
|
||||
|
||||
;; ** Communication
|
||||
|
||||
;; Data is sent to the API via a central class method. For convenience, there is a ~send-with-session~ variant that automatically adds the session id.
|
||||
|
||||
;; #+name: wd-send
|
||||
|
||||
;; [[file:webdriver.org::wd-send][wd-send]]
|
||||
(define-method (send (instance <WebDriver>) data uri method)
|
||||
(let* ((remote (string-append "http://" (slot-value instance 'server) ":" (->string (slot-value instance 'port)) "/"))
|
||||
(result (postprocess-result instance
|
||||
(with-input-from-request
|
||||
(make-request #:method method
|
||||
#:uri (uri-reference (string-append remote uri))
|
||||
#:headers (headers `((content-type application/json))))
|
||||
(if data (json->string data) "")
|
||||
read-json))))
|
||||
(if (and (list? result) (alist-ref/default result 'error #f))
|
||||
(wd-throw result)
|
||||
result)))
|
||||
|
||||
(define-method (send-with-session (instance <WebDriver>) data uri method)
|
||||
(send instance data (string-append "session/" (slot-value instance 'session-id) "/" uri) method))
|
||||
;; wd-send ends here
|
||||
|
||||
|
||||
|
||||
;; ** Session management
|
||||
|
||||
;; Session management is very simple. There is just one method to initialize a new session. Everything else is handled automatically.
|
||||
|
||||
;; #+name: wd-init-session
|
||||
|
||||
;; [[file:webdriver.org::wd-init-session][wd-init-session]]
|
||||
(define-method (initialize-session (instance <WebDriver>))
|
||||
(let ((result (send instance (construct-capabilities instance) "session" 'POST)))
|
||||
(set! (slot-value instance 'session-id) (alist-ref result 'sessionId))))
|
||||
;; wd-init-session ends here
|
||||
|
||||
|
||||
|
||||
;; #+name: wd-term-session
|
||||
|
||||
;; [[file:webdriver.org::wd-term-session][wd-term-session]]
|
||||
(define-method (terminate-session (instance <WebDriver>))
|
||||
(when (slot-value instance 'session-id)
|
||||
(send instance #f (string-append "session/" (slot-value instance 'session-id)) 'DELETE))
|
||||
(set! (slot-value instance 'session-id) #f))
|
||||
;; wd-term-session ends here
|
||||
|
||||
|
||||
|
||||
;; #+RESULTS: wd-session-test
|
||||
;; : -- testing session -----------------------------------------------------------
|
||||
;; : Initial state ........................................................ [ PASS]
|
||||
;; : Session id check ..................................................... [ PASS]
|
||||
;; : Session id after termination ......................................... [ PASS]
|
||||
;; : 3 tests completed in 3.788 seconds.
|
||||
;; : 3 out of 3 (100%) tests passed.
|
||||
;; : -- done testing session ------------------------------------------------------
|
||||
|
||||
;; ** API Access Methods
|
||||
|
||||
;; #+name: wd-url
|
||||
|
||||
;; [[file:webdriver.org::wd-url][wd-url]]
|
||||
(define-method (set-url (instance <WebDriver>) url)
|
||||
(send-with-session instance `((url . ,url)) "url" 'POST))
|
||||
|
||||
(define-method (url (instance <WebDriver>))
|
||||
(send-with-session instance #f "url" 'GET))
|
||||
;; wd-url ends here
|
||||
|
||||
|
||||
|
||||
;; #+RESULTS: wd-url-test
|
||||
;; : -- testing url ---------------------------------------------------------------
|
||||
;; : Initial state ........................................................ [ PASS]
|
||||
;; : Navigating to the first website ...................................... [ PASS]
|
||||
;; : 2 tests completed in 5.247 seconds.
|
||||
;; : 2 out of 2 (100%) tests passed.
|
||||
;; : -- done testing url ----------------------------------------------------------
|
||||
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:3]]
|
||||
(define-method (back (instance <WebDriver>))
|
||||
(send-with-session instance #f "back" 'POST))
|
||||
;; API Access Methods:3 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:4]]
|
||||
(define-method (forward (instance <WebDriver>))
|
||||
(send-with-session instance #f "forward" 'POST))
|
||||
;; API Access Methods:4 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:5]]
|
||||
(define-method (refresh (instance <WebDriver>))
|
||||
(send-with-session instance #f "refresh" 'POST))
|
||||
;; API Access Methods:5 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:6]]
|
||||
(define-method (title (instance <WebDriver>))
|
||||
(send-with-session instance #f "title" 'GET))
|
||||
;; API Access Methods:6 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:7]]
|
||||
(define-method (status (instance <WebDriver>))
|
||||
(send-with-session instance #f "status" 'GET))
|
||||
;; API Access Methods:7 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:8]]
|
||||
(define-method (source (instance <WebDriver>))
|
||||
(send-with-session instance #f "source" 'GET))
|
||||
;; API Access Methods:8 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:9]]
|
||||
(define-method (screenshot (instance <WebDriver>))
|
||||
(base64-decode (send-with-session instance #f "screenshot" 'GET)))
|
||||
;; API Access Methods:9 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:10]]
|
||||
(define-method (print-page (instance <WebDriver>))
|
||||
(send-with-session instance #f "print" 'POST))
|
||||
;; API Access Methods:10 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:11]]
|
||||
(define-method (execute-async (instance <WebDriver>) script args)
|
||||
(send-with-session instance `((script . ,script) (args . ,args)) "execute/async" 'POST))
|
||||
;; API Access Methods:11 ends here
|
||||
|
||||
;; [[file:webdriver.org::*API Access Methods][API Access Methods:12]]
|
||||
(define-method (execute-sync (instance <WebDriver>) script args)
|
||||
(send-with-session instance `((script . ,script) (args . ,args)) "execute/sync" 'POST))
|
||||
;; API Access Methods:12 ends here
|
||||
|
||||
|
||||
|
||||
;; The following timeouts are defined:
|
||||
|
||||
;; - =script=: defaults to 30'000, specifies when to interrupt a script that is being evaluated. A nil value implies that scripts should never be interrupted, but instead run indefinitely.
|
||||
;; - =pageLoad=: defaults to 300'000, provides the timeout limit used to interrupt an explicit navigation attempt.
|
||||
;; - =implicit=: defaults to 0, specifies a time to wait for the element location strategy to complete when locating an element.
|
||||
|
||||
|
||||
;; [[file:webdriver.org::*Timeouts][Timeouts:1]]
|
||||
(define-class <WDTimeouts> ()
|
||||
((script 30000)
|
||||
(pageLoad 300000)
|
||||
(implicit 0)))
|
||||
;; Timeouts:1 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Timeouts][Timeouts:2]]
|
||||
(define-method (extract (instance <WDTimeouts>))
|
||||
`((script . ,(slot-value instance 'script))
|
||||
(pageLoad . ,(slot-value instance 'pageLoad))
|
||||
(implicit . ,(slot-value instance 'implicit))))
|
||||
;; Timeouts:2 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Setting and getting timeouts][Setting and getting timeouts:1]]
|
||||
(define-method (set-timeouts (instance <WebDriver>) (timeouts <WDTimeouts>))
|
||||
(send-with-session instance (extract timeouts) "timeouts" 'POST))
|
||||
|
||||
(define-method (timeouts (instance <WebDriver>))
|
||||
(let ((result (send-with-session instance #f "timeouts" 'GET)))
|
||||
(make <WDTimeouts>
|
||||
'script (alist-ref result 'script)
|
||||
'pageLoad (alist-ref result 'pageLoad)
|
||||
'implicit (alist-ref result 'implicit))))
|
||||
;; Setting and getting timeouts:1 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Element Class][Element Class:1]]
|
||||
(define-class <WDElement> ()
|
||||
((driver #f)
|
||||
(element #f)))
|
||||
;; Element Class:1 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Element Class][Element Class:2]]
|
||||
(define-method (send-with-session (instance <WDElement>) data uri method)
|
||||
(send-with-session (slot-value instance 'driver) data
|
||||
(string-append "element/" (slot-value instance 'element) "/" uri)
|
||||
method))
|
||||
;; Element Class:2 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Location Strategies][Location Strategies:1]]
|
||||
(define css-selector "css selector")
|
||||
(define link-text "link text")
|
||||
(define partial-link-text "partial link text")
|
||||
(define tag-name "tag name")
|
||||
(define xpath "xpath")
|
||||
;; Location Strategies:1 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Accessor Methods][Accessor Methods:1]]
|
||||
(define-method (find-element (instance <WebDriver>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "element" 'POST)))
|
||||
(make <WDElement> 'driver instance 'element (car (alist-values result)))
|
||||
element))
|
||||
;; Accessor Methods:1 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Accessor Methods][Accessor Methods:2]]
|
||||
(define-method (find-elements (instance <WebDriver>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "elements" 'POST)))
|
||||
(map
|
||||
(lambda (elem)
|
||||
(make <WDElement> 'driver instance 'element (car (alist-values elem))))
|
||||
result)))
|
||||
;; Accessor Methods:2 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Accessor Methods][Accessor Methods:3]]
|
||||
(define-method (find-element (instance <WDElement>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "element" 'POST)))
|
||||
(make <WDElement> 'driver (slot-value instance 'driver) 'element (car (alist-values result)))
|
||||
element))
|
||||
;; Accessor Methods:3 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Accessor Methods][Accessor Methods:4]]
|
||||
(define-method (find-elements (instance <WDElement>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "elements" 'POST)))
|
||||
(map
|
||||
(lambda (elem)
|
||||
(make <WDElement> 'driver (slot-value instance 'driver) 'element (car (alist-values elem))))
|
||||
result)))
|
||||
;; Accessor Methods:4 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:1]]
|
||||
(define-method (attribute (instance <WDElement>) attribute)
|
||||
(let ((result (send-with-session instance #f
|
||||
(string-append "attribute/" attribute)
|
||||
'GET)))
|
||||
(if (equal? "true" result)
|
||||
#t
|
||||
result)))
|
||||
;; Working with Elements:1 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:2]]
|
||||
(define-method (property (instance <WDElement>) property)
|
||||
(send-with-session instance #f (string-append "property/" property) 'GET))
|
||||
;; Working with Elements:2 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:3]]
|
||||
(define-method (clear (instance <WDElement>))
|
||||
(send-with-session instance #f "clear" 'POST))
|
||||
;; Working with Elements:3 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:4]]
|
||||
(define-method (click (instance <WDElement>))
|
||||
(send-with-session instance #f "click" 'POST))
|
||||
;; Working with Elements:4 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:5]]
|
||||
(define-method (computed-label (instance <WDElement>))
|
||||
(send-with-session instance #f "computedlabel" 'GET))
|
||||
;; Working with Elements:5 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:6]]
|
||||
(define-method (computed-role (instance <WDElement>))
|
||||
(send-with-session instance #f "computedrole" 'GET))
|
||||
;; Working with Elements:6 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:7]]
|
||||
(define-method (enabled? (instance <WDElement>))
|
||||
(send-with-session instance #f "enabled" 'GET))
|
||||
;; Working with Elements:7 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:8]]
|
||||
(define-method (selected? (instance <WDElement>))
|
||||
(send-with-session instance #f "selected" 'GET))
|
||||
;; Working with Elements:8 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:9]]
|
||||
(define-method (name (instance <WDElement>))
|
||||
(send-with-session instance #f "name" 'GET))
|
||||
;; Working with Elements:9 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:10]]
|
||||
(define-method (rect (instance <WDElement>))
|
||||
(send-with-session instance #f "rect" 'GET))
|
||||
;; Working with Elements:10 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:11]]
|
||||
(define-method (screenshot (instance <WDElement>))
|
||||
(base64-decode (send-with-session instance #f "screenshot" 'GET)))
|
||||
;; Working with Elements:11 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:12]]
|
||||
(define-method (text (instance <WDElement>))
|
||||
(send-with-session instance #f "text" 'GET))
|
||||
;; Working with Elements:12 ends here
|
||||
|
||||
;; [[file:webdriver.org::*Working with Elements][Working with Elements:13]]
|
||||
(define-method (set-value (instance <WDElement>) value)
|
||||
(send-with-session instance `((text . ,value)) "value" 'POST))
|
||||
;; Working with Elements:13 ends here
|
14
webdriver.egg
Normal file
14
webdriver.egg
Normal file
|
@ -0,0 +1,14 @@
|
|||
;; [[file:webdriver.org::*About This Egg][About This Egg:1]]
|
||||
;; -*- scheme -*-
|
||||
((author "Daniel Ziltener")
|
||||
(synopsis "A WebDriver API implementation for Chicken")
|
||||
(category web)
|
||||
(license "BSD")
|
||||
(version "0.5")
|
||||
(dependencies r7rs srfi-34 srfi-35 base64 http-client intarweb uri-common coops alist-lib medea)
|
||||
(test-dependencies test)
|
||||
|
||||
(components
|
||||
(extension webdriver
|
||||
(csc-options "-X" "r7rs" "-R" "r7rs" "-sJ"))))
|
||||
;; About This Egg:1 ends here
|
957
webdriver.html
Normal file
957
webdriver.html
Normal file
|
@ -0,0 +1,957 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<!-- 2023-04-13 Do 16:13 -->
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Webdriver implementation in Chicken Scheme</title>
|
||||
<meta name="author" content="Daniel Ziltener" />
|
||||
<meta name="generator" content="Org Mode" />
|
||||
<style>
|
||||
#content { max-width: 60em; margin: auto; }
|
||||
.title { text-align: center;
|
||||
margin-bottom: .2em; }
|
||||
.subtitle { text-align: center;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
margin-top:0; }
|
||||
.todo { font-family: monospace; color: red; }
|
||||
.done { font-family: monospace; color: green; }
|
||||
.priority { font-family: monospace; color: orange; }
|
||||
.tag { background-color: #eee; font-family: monospace;
|
||||
padding: 2px; font-size: 80%; font-weight: normal; }
|
||||
.timestamp { color: #bebebe; }
|
||||
.timestamp-kwd { color: #5f9ea0; }
|
||||
.org-right { margin-left: auto; margin-right: 0px; text-align: right; }
|
||||
.org-left { margin-left: 0px; margin-right: auto; text-align: left; }
|
||||
.org-center { margin-left: auto; margin-right: auto; text-align: center; }
|
||||
.underline { text-decoration: underline; }
|
||||
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
|
||||
p.verse { margin-left: 3%; }
|
||||
pre {
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 3px;
|
||||
background-color: #f2f2f2;
|
||||
padding: 8pt;
|
||||
font-family: monospace;
|
||||
overflow: auto;
|
||||
margin: 1.2em;
|
||||
}
|
||||
pre.src {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
pre.src:before {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: 12px;
|
||||
padding: 3px;
|
||||
color: #555;
|
||||
background-color: #f2f2f299;
|
||||
}
|
||||
pre.src:hover:before { display: inline; margin-top: 14px;}
|
||||
/* Languages per Org manual */
|
||||
pre.src-asymptote:before { content: 'Asymptote'; }
|
||||
pre.src-awk:before { content: 'Awk'; }
|
||||
pre.src-authinfo::before { content: 'Authinfo'; }
|
||||
pre.src-C:before { content: 'C'; }
|
||||
/* pre.src-C++ doesn't work in CSS */
|
||||
pre.src-clojure:before { content: 'Clojure'; }
|
||||
pre.src-css:before { content: 'CSS'; }
|
||||
pre.src-D:before { content: 'D'; }
|
||||
pre.src-ditaa:before { content: 'ditaa'; }
|
||||
pre.src-dot:before { content: 'Graphviz'; }
|
||||
pre.src-calc:before { content: 'Emacs Calc'; }
|
||||
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
|
||||
pre.src-fortran:before { content: 'Fortran'; }
|
||||
pre.src-gnuplot:before { content: 'gnuplot'; }
|
||||
pre.src-haskell:before { content: 'Haskell'; }
|
||||
pre.src-hledger:before { content: 'hledger'; }
|
||||
pre.src-java:before { content: 'Java'; }
|
||||
pre.src-js:before { content: 'Javascript'; }
|
||||
pre.src-latex:before { content: 'LaTeX'; }
|
||||
pre.src-ledger:before { content: 'Ledger'; }
|
||||
pre.src-lisp:before { content: 'Lisp'; }
|
||||
pre.src-lilypond:before { content: 'Lilypond'; }
|
||||
pre.src-lua:before { content: 'Lua'; }
|
||||
pre.src-matlab:before { content: 'MATLAB'; }
|
||||
pre.src-mscgen:before { content: 'Mscgen'; }
|
||||
pre.src-ocaml:before { content: 'Objective Caml'; }
|
||||
pre.src-octave:before { content: 'Octave'; }
|
||||
pre.src-org:before { content: 'Org mode'; }
|
||||
pre.src-oz:before { content: 'OZ'; }
|
||||
pre.src-plantuml:before { content: 'Plantuml'; }
|
||||
pre.src-processing:before { content: 'Processing.js'; }
|
||||
pre.src-python:before { content: 'Python'; }
|
||||
pre.src-R:before { content: 'R'; }
|
||||
pre.src-ruby:before { content: 'Ruby'; }
|
||||
pre.src-sass:before { content: 'Sass'; }
|
||||
pre.src-scheme:before { content: 'Scheme'; }
|
||||
pre.src-screen:before { content: 'Gnu Screen'; }
|
||||
pre.src-sed:before { content: 'Sed'; }
|
||||
pre.src-sh:before { content: 'shell'; }
|
||||
pre.src-sql:before { content: 'SQL'; }
|
||||
pre.src-sqlite:before { content: 'SQLite'; }
|
||||
/* additional languages in org.el's org-babel-load-languages alist */
|
||||
pre.src-forth:before { content: 'Forth'; }
|
||||
pre.src-io:before { content: 'IO'; }
|
||||
pre.src-J:before { content: 'J'; }
|
||||
pre.src-makefile:before { content: 'Makefile'; }
|
||||
pre.src-maxima:before { content: 'Maxima'; }
|
||||
pre.src-perl:before { content: 'Perl'; }
|
||||
pre.src-picolisp:before { content: 'Pico Lisp'; }
|
||||
pre.src-scala:before { content: 'Scala'; }
|
||||
pre.src-shell:before { content: 'Shell Script'; }
|
||||
pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
|
||||
/* additional language identifiers per "defun org-babel-execute"
|
||||
in ob-*.el */
|
||||
pre.src-cpp:before { content: 'C++'; }
|
||||
pre.src-abc:before { content: 'ABC'; }
|
||||
pre.src-coq:before { content: 'Coq'; }
|
||||
pre.src-groovy:before { content: 'Groovy'; }
|
||||
/* additional language identifiers from org-babel-shell-names in
|
||||
ob-shell.el: ob-shell is the only babel language using a lambda to put
|
||||
the execution function name together. */
|
||||
pre.src-bash:before { content: 'bash'; }
|
||||
pre.src-csh:before { content: 'csh'; }
|
||||
pre.src-ash:before { content: 'ash'; }
|
||||
pre.src-dash:before { content: 'dash'; }
|
||||
pre.src-ksh:before { content: 'ksh'; }
|
||||
pre.src-mksh:before { content: 'mksh'; }
|
||||
pre.src-posh:before { content: 'posh'; }
|
||||
/* Additional Emacs modes also supported by the LaTeX listings package */
|
||||
pre.src-ada:before { content: 'Ada'; }
|
||||
pre.src-asm:before { content: 'Assembler'; }
|
||||
pre.src-caml:before { content: 'Caml'; }
|
||||
pre.src-delphi:before { content: 'Delphi'; }
|
||||
pre.src-html:before { content: 'HTML'; }
|
||||
pre.src-idl:before { content: 'IDL'; }
|
||||
pre.src-mercury:before { content: 'Mercury'; }
|
||||
pre.src-metapost:before { content: 'MetaPost'; }
|
||||
pre.src-modula-2:before { content: 'Modula-2'; }
|
||||
pre.src-pascal:before { content: 'Pascal'; }
|
||||
pre.src-ps:before { content: 'PostScript'; }
|
||||
pre.src-prolog:before { content: 'Prolog'; }
|
||||
pre.src-simula:before { content: 'Simula'; }
|
||||
pre.src-tcl:before { content: 'tcl'; }
|
||||
pre.src-tex:before { content: 'TeX'; }
|
||||
pre.src-plain-tex:before { content: 'Plain TeX'; }
|
||||
pre.src-verilog:before { content: 'Verilog'; }
|
||||
pre.src-vhdl:before { content: 'VHDL'; }
|
||||
pre.src-xml:before { content: 'XML'; }
|
||||
pre.src-nxml:before { content: 'XML'; }
|
||||
/* add a generic configuration mode; LaTeX export needs an additional
|
||||
(add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
|
||||
pre.src-conf:before { content: 'Configuration File'; }
|
||||
|
||||
table { border-collapse:collapse; }
|
||||
caption.t-above { caption-side: top; }
|
||||
caption.t-bottom { caption-side: bottom; }
|
||||
td, th { vertical-align:top; }
|
||||
th.org-right { text-align: center; }
|
||||
th.org-left { text-align: center; }
|
||||
th.org-center { text-align: center; }
|
||||
td.org-right { text-align: right; }
|
||||
td.org-left { text-align: left; }
|
||||
td.org-center { text-align: center; }
|
||||
dt { font-weight: bold; }
|
||||
.footpara { display: inline; }
|
||||
.footdef { margin-bottom: 1em; }
|
||||
.figure { padding: 1em; }
|
||||
.figure p { text-align: center; }
|
||||
.equation-container {
|
||||
display: table;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
.equation {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.equation-label {
|
||||
display: table-cell;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.inlinetask {
|
||||
padding: 10px;
|
||||
border: 2px solid gray;
|
||||
margin: 10px;
|
||||
background: #ffffcc;
|
||||
}
|
||||
#org-div-home-and-up
|
||||
{ text-align: right; font-size: 70%; white-space: nowrap; }
|
||||
textarea { overflow-x: auto; }
|
||||
.linenr { font-size: smaller }
|
||||
.code-highlighted { background-color: #ffff00; }
|
||||
.org-info-js_info-navigation { border-style: none; }
|
||||
#org-info-js_console-label
|
||||
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
|
||||
.org-info-js_search-highlight
|
||||
{ background-color: #ffff00; color: #000000; font-weight: bold; }
|
||||
.org-svg { }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content" class="content">
|
||||
<h1 class="title">Webdriver implementation in Chicken Scheme</h1>
|
||||
<div id="table-of-contents" role="doc-toc">
|
||||
<h2>Table of Contents</h2>
|
||||
<div id="text-table-of-contents" role="doc-toc">
|
||||
<ul>
|
||||
<li><a href="#org4399fba">1. Dependencies</a></li>
|
||||
<li><a href="#org686fad8">2. Error Conditions</a></li>
|
||||
<li><a href="#orgb911463">3. WebDriver</a>
|
||||
<ul>
|
||||
<li><a href="#org3d13a0c">3.1. Geckodriver</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#orgf472d01">4. WebDriver API</a>
|
||||
<ul>
|
||||
<li><a href="#org5685400">4.1. Communication</a></li>
|
||||
<li><a href="#orgd3b1c20">4.2. Session management</a></li>
|
||||
<li><a href="#orgca687e8">4.3. API Access Methods</a></li>
|
||||
<li><a href="#org8175c38">4.4. Timeouts</a>
|
||||
<ul>
|
||||
<li><a href="#org2be29cc">4.4.1. Setting and getting timeouts</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#org0b96887">4.5. Elements</a>
|
||||
<ul>
|
||||
<li><a href="#org3abeb2f">4.5.1. Element Class</a></li>
|
||||
<li><a href="#orgf5ba942">4.5.2. Finding Elements</a></li>
|
||||
<li><a href="#org25c6507">4.5.3. Working with Elements</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="outline-container-org4399fba" class="outline-2">
|
||||
<h2 id="org4399fba"><span class="section-number-2">1.</span> Dependencies</h2>
|
||||
<div class="outline-text-2" id="text-1">
|
||||
<table id="orgb55ce99" border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
|
||||
|
||||
|
||||
<colgroup>
|
||||
<col class="org-left" />
|
||||
|
||||
<col class="org-left" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="org-left">Dependency</th>
|
||||
<th scope="col" class="org-left">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="org-left">srfi-34</td>
|
||||
<td class="org-left">Exception Handling</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">srfi-35</td>
|
||||
<td class="org-left">Exception Types</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">base64</td>
|
||||
<td class="org-left">decoding screenshot data</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">http-client</td>
|
||||
<td class="org-left">API interaction</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">intarweb</td>
|
||||
<td class="org-left">Supporting HTTP functionality</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">uri-common</td>
|
||||
<td class="org-left">Supporting HTTP functionality</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">coops</td>
|
||||
<td class="org-left">Object system</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">alist-lib</td>
|
||||
<td class="org-left">Handling alists from JSON objects</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">medea</td>
|
||||
<td class="org-left">JSON handling</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-org686fad8" class="outline-2">
|
||||
<h2 id="org686fad8"><span class="section-number-2">2.</span> Error Conditions</h2>
|
||||
<div class="outline-text-2" id="text-2">
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="orgca58a9e"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-condition-type</span> &wd-exception &error wd-exception?
|
||||
<span style="color: #c49619;">(</span>stacktrace wd-stacktrace<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>data wd-data<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Every API error code (key “error” in the returned JSON data) gets its own condition type, prefixed by <code>&</code>. They all inherit from <code>&wd-exception</code>.
|
||||
</p>
|
||||
|
||||
<table id="org1b76a29" border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
|
||||
|
||||
|
||||
<colgroup>
|
||||
<col class="org-left" />
|
||||
|
||||
<col class="org-left" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="org-left">Name</th>
|
||||
<th scope="col" class="org-left">API Error Code</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="org-left">detached-shadow-root</td>
|
||||
<td class="org-left">detached shadow root</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">element-click-intercepted</td>
|
||||
<td class="org-left">element click intercepted</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">element-not-interactable</td>
|
||||
<td class="org-left">element not interactable</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">insecure-certificate</td>
|
||||
<td class="org-left">insecure certificate</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">invalid-argument</td>
|
||||
<td class="org-left">invalid argument</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">invalid-cookie-domain</td>
|
||||
<td class="org-left">invalid cookie domain</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">invalid-element-state</td>
|
||||
<td class="org-left">invalid element state</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">invalid-selector</td>
|
||||
<td class="org-left">invalid selector</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">invalid-session-id</td>
|
||||
<td class="org-left">invalid session id</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">javascript-error</td>
|
||||
<td class="org-left">javascript error</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">move-target-out-of-bounds</td>
|
||||
<td class="org-left">move target out of bounds</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">no-such-alert</td>
|
||||
<td class="org-left">no such alert</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">no-such-cookie</td>
|
||||
<td class="org-left">no such cookie</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">no-such-element</td>
|
||||
<td class="org-left">no such element</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">no-such-frame</td>
|
||||
<td class="org-left">no such frame</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">no-such-shadow-root</td>
|
||||
<td class="org-left">no such shadow root</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">no-such-window</td>
|
||||
<td class="org-left">no such window</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">script-timeout</td>
|
||||
<td class="org-left">script timeout</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">session-not-created</td>
|
||||
<td class="org-left">session not created</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">stale-element-reference</td>
|
||||
<td class="org-left">stale element reference</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">timeout</td>
|
||||
<td class="org-left">timeout</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">unable-to-capture-screen</td>
|
||||
<td class="org-left">unable to capture screen</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">unable-to-set-cookie</td>
|
||||
<td class="org-left">unable to set cookie</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">unexpected-alert-open</td>
|
||||
<td class="org-left">unexpected alert open</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">unknown-command</td>
|
||||
<td class="org-left">unknown command</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">unknown-error</td>
|
||||
<td class="org-left">unknown error</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">unknown-method</td>
|
||||
<td class="org-left">unknown method</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="org-left">unsupported-operation</td>
|
||||
<td class="org-left">unsupported operation</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-orgb911463" class="outline-2">
|
||||
<h2 id="orgb911463"><span class="section-number-2">3.</span> WebDriver</h2>
|
||||
<div class="outline-text-2" id="text-3">
|
||||
<p>
|
||||
The core element of the library is the <code><WebDriver></code> class and its subclasses. The class has the following fields:
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="orgdcaa051"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-class</span> <span style="color: #c49619; font-style: italic;"><WebDriver></span> <span style="color: #c49619;">()</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>browser #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>active? #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>browser-pid #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>server #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>port #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>session-id #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>prefs #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>capabilities #f<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The parent class provides a handful of methods, but does not implement all of them; some are the sole responsibility of the subclass. The <code>launch</code> method, on the other hand, bears shared responsibility. It sets a finalizer to ensure termination of the web driver process in case the class is disposed of with a still-open driver.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="org47d44ea"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">launch</span> <span style="color: #3c98e0; font-style: italic;">#:after</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> options<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>set-finalizer! instance <span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">lambda</span> <span style="color: #93a61a;">(</span>obj<span style="color: #93a61a;">)</span>
|
||||
<span style="color: #93a61a;">(</span><span style="color: #93a61a; font-weight: bold;">when</span> <span style="color: #3c98e0;">(</span>slot-value instance 'active?<span style="color: #3c98e0;">)</span>
|
||||
<span style="color: #3c98e0;">(</span>terminate instance<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">terminate</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>terminate-session instance<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>process-signal <span style="color: #db5823;">(</span>slot-value instance 'browser-pid<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">set!</span> <span style="color: #db5823;">(</span>slot-value instance 'browser-pid<span style="color: #db5823;">)</span> #f<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">set!</span> <span style="color: #db5823;">(</span>slot-value instance 'active?<span style="color: #db5823;">)</span> #f<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">construct-capabilities</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> #!optional caps<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>raise 'subclass-responsibility<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">postprocess-result</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> result<span style="color: #c49619;">)</span>
|
||||
result<span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Main initialization is done by calling the <code>new-WebDriver</code> procedure with the respective class name and optionally an alist of options.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="org5798eb1"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">new-WebDriver</span> browser #!optional options<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>instance <span style="color: #3c98e0;">(</span>make browser<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>launch instance options<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>sleep <span style="color: #7a7ed2; font-weight: bold;">1</span><span style="color: #db5823;">)</span>
|
||||
instance<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-org3d13a0c" class="outline-3">
|
||||
<h3 id="org3d13a0c"><span class="section-number-3">3.1.</span> Geckodriver</h3>
|
||||
<div class="outline-text-3" id="text-3-1">
|
||||
<p>
|
||||
The Geckodriver is used to control Firefox.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="orge7a717b"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-class</span> <span style="color: #c49619; font-style: italic;"><Gecko></span> <span style="color: #c49619;">(</span><span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>browser <span style="color: #3c98e0; font-style: italic;">#:firefox</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>server <span style="color: #93a61a;">"127.0.0.1"</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>port <span style="color: #7a7ed2; font-weight: bold;">4444</span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">launch</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><Gecko></span><span style="color: #db5823;">)</span> options<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>pid <span style="color: #3c98e0;">(</span>process-run <span style="color: #93a61a;">"geckodriver > /dev/null 2>&1"</span><span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">set!</span> <span style="color: #93a61a;">(</span>slot-value instance 'browser-pid<span style="color: #93a61a;">)</span> pid<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">set!</span> <span style="color: #93a61a;">(</span>slot-value instance 'active?<span style="color: #93a61a;">)</span> #t<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">set!</span> <span style="color: #93a61a;">(</span>slot-value instance 'capabilities<span style="color: #93a61a;">)</span> options<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The capabilities object for Geckodriver is of the form <code>{"capabilities": {...}}</code>.
|
||||
For more information on capabilities, see <a href="https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities">https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities</a>.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="org3337880"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">construct-capabilities</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><Gecko></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>caps <span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">or</span> <span style="color: #c49619;">(</span>slot-value instance 'capabilities<span style="color: #c49619;">)</span> <span style="color: #c49619;">(</span>list<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
`<span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>capabilities . ,caps<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Sometimes, Geckodriver returns the results of a command in a JSON object with the sole key <code>"value"</code>. We have to correct that before returning the data to the user.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="org75d89a5"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">postprocess-result</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><Gecko></span><span style="color: #db5823;">)</span> result<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>alist-ref/default result 'value result<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-orgf472d01" class="outline-2">
|
||||
<h2 id="orgf472d01"><span class="section-number-2">4.</span> WebDriver API</h2>
|
||||
<div class="outline-text-2" id="text-4">
|
||||
</div>
|
||||
<div id="outline-container-org5685400" class="outline-3">
|
||||
<h3 id="org5685400"><span class="section-number-3">4.1.</span> Communication</h3>
|
||||
<div class="outline-text-3" id="text-4-1">
|
||||
<p>
|
||||
Data is sent to the API via a central class method. For convenience, there is a <code>send-with-session</code> variant that automatically adds the session id.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="orgaf961e8"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">send</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> data uri method<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let*</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>remote <span style="color: #3c98e0;">(</span>string-append <span style="color: #93a61a;">"http://"</span> <span style="color: #c49619;">(</span>slot-value instance 'server<span style="color: #c49619;">)</span> <span style="color: #93a61a;">":"</span> <span style="color: #c49619;">(</span>->string <span style="color: #db5823;">(</span>slot-value instance 'port<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span> <span style="color: #93a61a;">"/"</span><span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span>
|
||||
<span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>postprocess-result instance
|
||||
<span style="color: #c49619;">(</span>with-input-from-request
|
||||
<span style="color: #db5823;">(</span>make-request <span style="color: #3c98e0; font-style: italic;">#:method</span> method
|
||||
<span style="color: #3c98e0; font-style: italic;">#:uri</span> <span style="color: #93a61a;">(</span>uri-reference <span style="color: #3c98e0;">(</span>string-append remote uri<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span>
|
||||
<span style="color: #3c98e0; font-style: italic;">#:headers</span> <span style="color: #93a61a;">(</span>headers `<span style="color: #3c98e0;">(</span><span style="color: #c49619;">(</span>content-type application/json<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">if</span> data <span style="color: #93a61a;">(</span>json->string data<span style="color: #93a61a;">)</span> <span style="color: #93a61a;">""</span><span style="color: #db5823;">)</span>
|
||||
read-json<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">if</span> <span style="color: #93a61a;">(</span><span style="color: #93a61a; font-weight: bold;">and</span> <span style="color: #3c98e0;">(</span>list? result<span style="color: #3c98e0;">)</span> <span style="color: #3c98e0;">(</span>alist-ref/default result 'error #f<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span>
|
||||
<span style="color: #93a61a;">(</span>wd-throw result<span style="color: #93a61a;">)</span>
|
||||
result<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">send-with-session</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> data uri method<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send instance data <span style="color: #db5823;">(</span>string-append <span style="color: #93a61a;">"session/"</span> <span style="color: #93a61a;">(</span>slot-value instance 'session-id<span style="color: #93a61a;">)</span> <span style="color: #93a61a;">"/"</span> uri<span style="color: #db5823;">)</span> method<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-orgd3b1c20" class="outline-3">
|
||||
<h3 id="orgd3b1c20"><span class="section-number-3">4.2.</span> Session management</h3>
|
||||
<div class="outline-text-3" id="text-4-2">
|
||||
<p>
|
||||
Session management is very simple. There is just one method to initialize a new session. Everything else is handled automatically.
|
||||
</p>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="org9cfce71"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">initialize-session</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>send instance <span style="color: #c49619;">(</span>construct-capabilities instance<span style="color: #c49619;">)</span> <span style="color: #93a61a;">"session"</span> 'POST<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">set!</span> <span style="color: #93a61a;">(</span>slot-value instance 'session-id<span style="color: #93a61a;">)</span> <span style="color: #93a61a;">(</span>alist-ref result 'sessionId<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="org73e6879"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">terminate-session</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">when</span> <span style="color: #db5823;">(</span>slot-value instance 'session-id<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>send instance #f <span style="color: #93a61a;">(</span>string-append <span style="color: #93a61a;">"session/"</span> <span style="color: #3c98e0;">(</span>slot-value instance 'session-id<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span> 'DELETE<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">set!</span> <span style="color: #db5823;">(</span>slot-value instance 'session-id<span style="color: #db5823;">)</span> #f<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<pre class="example">
|
||||
-- testing session -----------------------------------------------------------
|
||||
Initial state ........................................................ [ PASS]
|
||||
Session id check ..................................................... [ PASS]
|
||||
Session id after termination ......................................... [ PASS]
|
||||
3 tests completed in 4.952 seconds.
|
||||
3 out of 3 (100%) tests passed.
|
||||
-- done testing session ------------------------------------------------------
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-orgca687e8" class="outline-3">
|
||||
<h3 id="orgca687e8"><span class="section-number-3">4.3.</span> API Access Methods</h3>
|
||||
<div class="outline-text-3" id="text-4-3">
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme" id="org9ede152"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">set-url</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> url<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance `<span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>url . ,url<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span> <span style="color: #93a61a;">"url"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">url</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"url"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<pre class="example">
|
||||
-- testing url ---------------------------------------------------------------
|
||||
Initial state ........................................................ [ PASS]
|
||||
Navigating to the first website ...................................... [ PASS]
|
||||
2 tests completed in 5.471 seconds.
|
||||
2 out of 2 (100%) tests passed.
|
||||
-- done testing url ----------------------------------------------------------
|
||||
</pre>
|
||||
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">back</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"back"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">forward</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"forward"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">forward</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"refresh"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">title</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"title"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">status</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"status"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">source</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"source"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">screenshot</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>base64-decode <span style="color: #db5823;">(</span>send-with-session instance #f <span style="color: #93a61a;">"screenshot"</span> 'GET<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">print-page</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"print"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">execute-async</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> script args<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance `<span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>script . ,script<span style="color: #93a61a;">)</span> <span style="color: #93a61a;">(</span>args . ,args<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span> <span style="color: #93a61a;">"execute/async"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">execute-sync</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> script args<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance `<span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>script . ,script<span style="color: #93a61a;">)</span> <span style="color: #93a61a;">(</span>args . ,args<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span> <span style="color: #93a61a;">"execute/sync"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-org8175c38" class="outline-3">
|
||||
<h3 id="org8175c38"><span class="section-number-3">4.4.</span> Timeouts</h3>
|
||||
<div class="outline-text-3" id="text-4-4">
|
||||
<p>
|
||||
The following timeouts are defined:
|
||||
</p>
|
||||
|
||||
<ul class="org-ul">
|
||||
<li><code>script</code>: defaults to 30’000, specifies when to interrupt a script that is being evaluated. A nil value implies that scripts should never be interrupted, but instead run indefinitely.</li>
|
||||
<li><code>pageLoad</code>: defaults to 300’000, provides the timeout limit used to interrupt an explicit navigation attempt.</li>
|
||||
<li><code>implicit</code>: defaults to 0, specifies a time to wait for the element location strategy to complete when locating an element.</li>
|
||||
</ul>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-class</span> <span style="color: #c49619; font-style: italic;"><WDTimeouts></span> <span style="color: #c49619;">()</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>script <span style="color: #7a7ed2; font-weight: bold;">30000</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>pageLoad <span style="color: #7a7ed2; font-weight: bold;">300000</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>implicit <span style="color: #7a7ed2; font-weight: bold;">0</span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">extract</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDTimeouts></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
`<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>script . ,<span style="color: #93a61a;">(</span>slot-value instance 'script<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>pageLoad . ,<span style="color: #93a61a;">(</span>slot-value instance 'pageLoad<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>implicit . ,<span style="color: #93a61a;">(</span>slot-value instance 'implicit<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-org2be29cc" class="outline-4">
|
||||
<h4 id="org2be29cc"><span class="section-number-4">4.4.1.</span> Setting and getting timeouts</h4>
|
||||
<div class="outline-text-4" id="text-4-4-1">
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">set-timeouts</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> <span style="color: #db5823;">(</span>timeouts <span style="color: #c49619; font-style: italic;"><WDTimeouts></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance <span style="color: #db5823;">(</span>extract timeouts<span style="color: #db5823;">)</span> <span style="color: #93a61a;">"timeouts"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">timeouts</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>send-with-session instance #f <span style="color: #93a61a;">"timeouts"</span> 'GET<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>make <span style="color: #c49619; font-style: italic;"><WDTimeouts></span>
|
||||
'script <span style="color: #93a61a;">(</span>alist-ref result 'script<span style="color: #93a61a;">)</span>
|
||||
'pageLoad <span style="color: #93a61a;">(</span>alist-ref result 'pageLoad<span style="color: #93a61a;">)</span>
|
||||
'implicit <span style="color: #93a61a;">(</span>alist-ref result 'implicit<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-org0b96887" class="outline-3">
|
||||
<h3 id="org0b96887"><span class="section-number-3">4.5.</span> Elements</h3>
|
||||
<div class="outline-text-3" id="text-4-5">
|
||||
</div>
|
||||
<div id="outline-container-org3abeb2f" class="outline-4">
|
||||
<h4 id="org3abeb2f"><span class="section-number-4">4.5.1.</span> Element Class</h4>
|
||||
<div class="outline-text-4" id="text-4-5-1">
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-class</span> <span style="color: #c49619; font-style: italic;"><WDElement></span> <span style="color: #c49619;">()</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>driver #f<span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>element #f<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">send-with-session</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span> data uri method<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session <span style="color: #db5823;">(</span>slot-value instance 'driver<span style="color: #db5823;">)</span> data
|
||||
<span style="color: #db5823;">(</span>string-append <span style="color: #93a61a;">"element/"</span> <span style="color: #93a61a;">(</span>slot-value instance 'element<span style="color: #93a61a;">)</span> <span style="color: #93a61a;">"/"</span> uri<span style="color: #db5823;">)</span>
|
||||
method<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-orgf5ba942" class="outline-4">
|
||||
<h4 id="orgf5ba942"><span class="section-number-4">4.5.2.</span> Finding Elements</h4>
|
||||
<div class="outline-text-4" id="text-4-5-2">
|
||||
</div>
|
||||
<ol class="org-ol">
|
||||
<li><a id="org4cf0712"></a>Location Strategies<br />
|
||||
<div class="outline-text-5" id="text-4-5-2-1">
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define</span> <span style="color: #3c98e0;">css-selector</span> <span style="color: #93a61a;">"css selector"</span><span style="color: #3c98e0;">)</span>
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define</span> <span style="color: #3c98e0;">link-text</span> <span style="color: #93a61a;">"link text"</span><span style="color: #3c98e0;">)</span>
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define</span> <span style="color: #3c98e0;">partial-link-text</span> <span style="color: #93a61a;">"partial link text"</span><span style="color: #3c98e0;">)</span>
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define</span> <span style="color: #3c98e0;">tag-name</span> <span style="color: #93a61a;">"tag name"</span><span style="color: #3c98e0;">)</span>
|
||||
<span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define</span> <span style="color: #3c98e0;">xpath</span> <span style="color: #93a61a;">"xpath"</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li><a id="orga7a21a2"></a>Accessor Methods<br />
|
||||
<div class="outline-text-5" id="text-4-5-2-2">
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">find-element</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> strategy selector<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>send-with-session instance `<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>using . ,strategy<span style="color: #db5823;">)</span> <span style="color: #db5823;">(</span>value . ,selector<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span> <span style="color: #93a61a;">"element"</span> 'POST<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>make <span style="color: #c49619; font-style: italic;"><WDElement></span> 'driver instance 'element <span style="color: #93a61a;">(</span>car <span style="color: #3c98e0;">(</span>alist-values result<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
element<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">find-elements</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WebDriver></span><span style="color: #db5823;">)</span> strategy selector<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>send-with-session instance `<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>using . ,strategy<span style="color: #db5823;">)</span> <span style="color: #db5823;">(</span>value . ,selector<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span> <span style="color: #93a61a;">"elements"</span> 'POST<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">map</span>
|
||||
<span style="color: #93a61a;">(</span><span style="color: #93a61a; font-weight: bold;">lambda</span> <span style="color: #3c98e0;">(</span>elem<span style="color: #3c98e0;">)</span>
|
||||
<span style="color: #3c98e0;">(</span>make <span style="color: #c49619; font-style: italic;"><WDElement></span> 'driver instance 'element <span style="color: #c49619;">(</span>car <span style="color: #db5823;">(</span>alist-values elem<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span>
|
||||
result<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">find-element</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span> strategy selector<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>send-with-session instance `<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>using . ,strategy<span style="color: #db5823;">)</span> <span style="color: #db5823;">(</span>value . ,selector<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span> <span style="color: #93a61a;">"element"</span> 'POST<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span>make <span style="color: #c49619; font-style: italic;"><WDElement></span> 'driver <span style="color: #93a61a;">(</span>slot-value instance 'driver<span style="color: #93a61a;">)</span> 'element <span style="color: #93a61a;">(</span>car <span style="color: #3c98e0;">(</span>alist-values result<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
element<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">find-elements</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span> strategy selector<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>send-with-session instance `<span style="color: #c49619;">(</span><span style="color: #db5823;">(</span>using . ,strategy<span style="color: #db5823;">)</span> <span style="color: #db5823;">(</span>value . ,selector<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span> <span style="color: #93a61a;">"elements"</span> 'POST<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">map</span>
|
||||
<span style="color: #93a61a;">(</span><span style="color: #93a61a; font-weight: bold;">lambda</span> <span style="color: #3c98e0;">(</span>elem<span style="color: #3c98e0;">)</span>
|
||||
<span style="color: #3c98e0;">(</span>make <span style="color: #c49619; font-style: italic;"><WDElement></span> 'driver <span style="color: #c49619;">(</span>slot-value instance 'driver<span style="color: #c49619;">)</span> 'element <span style="color: #c49619;">(</span>car <span style="color: #db5823;">(</span>alist-values elem<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span>
|
||||
result<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-org25c6507" class="outline-4">
|
||||
<h4 id="org25c6507"><span class="section-number-4">4.5.3.</span> Working with Elements</h4>
|
||||
<div class="outline-text-4" id="text-4-5-3">
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">attribute</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span> attribute<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span><span style="color: #93a61a; font-weight: bold;">let</span> <span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>result <span style="color: #3c98e0;">(</span>send-with-session instance #f
|
||||
<span style="color: #c49619;">(</span>string-append <span style="color: #93a61a;">"attribute/"</span> attribute<span style="color: #c49619;">)</span>
|
||||
'GET<span style="color: #3c98e0;">)</span><span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span>
|
||||
<span style="color: #db5823;">(</span><span style="color: #93a61a; font-weight: bold;">if</span> <span style="color: #93a61a;">(</span>equal? <span style="color: #93a61a;">"true"</span> result<span style="color: #93a61a;">)</span>
|
||||
#t
|
||||
result<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">property</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span> property<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #db5823;">(</span>string-append <span style="color: #93a61a;">"property/"</span> property<span style="color: #db5823;">)</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">clear</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"clear"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">click</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"click"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">computed-label</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"computedlabel"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">computed-role</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"computedrole"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">enabled?</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"enabled"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">selected?</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"selected"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">name</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"name"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">rect</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"rect"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">screenshot</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>base64-decode <span style="color: #db5823;">(</span>send-with-session instance #f <span style="color: #93a61a;">"screenshot"</span> 'GET<span style="color: #db5823;">)</span><span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">text</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span><span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance #f <span style="color: #93a61a;">"text"</span> 'GET<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="org-src-container">
|
||||
<pre class="src src-scheme"><span style="color: #3c98e0;">(</span><span style="color: #93a61a; font-weight: bold;">define-method</span> <span style="color: #c49619;">(</span><span style="color: #3c98e0;">set-value</span> <span style="color: #db5823;">(</span>instance <span style="color: #c49619; font-style: italic;"><WDElement></span><span style="color: #db5823;">)</span> value<span style="color: #c49619;">)</span>
|
||||
<span style="color: #c49619;">(</span>send-with-session instance `<span style="color: #db5823;">(</span><span style="color: #93a61a;">(</span>text . ,value<span style="color: #93a61a;">)</span><span style="color: #db5823;">)</span> <span style="color: #93a61a;">"value"</span> 'POST<span style="color: #c49619;">)</span><span style="color: #3c98e0;">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="postamble" class="status">
|
||||
<p class="author">Author: Daniel Ziltener</p>
|
||||
<p class="date">Created: 2023-04-13 Do 16:13</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
657
webdriver.org
Normal file
657
webdriver.org
Normal file
|
@ -0,0 +1,657 @@
|
|||
#+title: Webdriver implementation in Chicken Scheme
|
||||
#+author: Daniel Ziltener
|
||||
#+property: header-args:scheme :session *chicken* :comments both
|
||||
|
||||
|
||||
* Helpers :noexport:
|
||||
:PROPERTIES:
|
||||
:header-args:scheme: :prologue "(import (chicken string))"
|
||||
:END:
|
||||
** Strip garbage from test results
|
||||
#+name: test-post
|
||||
#+begin_src scheme :var input='() :results output
|
||||
(for-each (lambda (str)
|
||||
(or (substring=? str ";")
|
||||
(substring=? str "Note")
|
||||
(print str)))
|
||||
(string-split input "\n"))
|
||||
#+end_src
|
||||
|
||||
** Prepare in-line testing
|
||||
#+name: prep-test
|
||||
#+begin_src scheme :noweb yes :tangle tests/run.scm :results silent
|
||||
(import r7rs
|
||||
test
|
||||
(chicken base)
|
||||
(chicken string)
|
||||
(chicken process)
|
||||
(chicken gc)
|
||||
<<dependencies-for-imports()>>
|
||||
)
|
||||
#+end_src
|
||||
|
||||
* Dependencies
|
||||
|
||||
#+name: dependencies
|
||||
| Dependency | Description |
|
||||
|-------------+-----------------------------------|
|
||||
| srfi-34 | Exception Handling |
|
||||
| srfi-35 | Exception Types |
|
||||
| base64 | decoding screenshot data |
|
||||
| http-client | API interaction |
|
||||
| intarweb | Supporting HTTP functionality |
|
||||
| uri-common | Supporting HTTP functionality |
|
||||
| coops | Object system |
|
||||
| alist-lib | Handling alists from JSON objects |
|
||||
| medea | JSON handling |
|
||||
|
||||
#+name: dependencies-for-egg
|
||||
#+begin_src emacs-lisp :var tbl=dependencies :colnames yes :results raw :exports none
|
||||
(mapconcat (lambda (row) (car row)) tbl " ")
|
||||
#+end_src
|
||||
|
||||
#+name: dependencies-for-imports
|
||||
#+begin_src emacs-lisp :var tbl=dependencies :colnames yes :results raw :exports none
|
||||
(mapconcat (lambda (row) (concat (car row) "\t\t;;" (cadr row)))
|
||||
tbl "\n")
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :noweb yes :tangle webdriver.scm :exports none
|
||||
(import r7rs)
|
||||
(define-library (webdriver)
|
||||
(import (scheme base))
|
||||
(export <WDElement>)
|
||||
(include "webdriver-impl.scm"))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :noweb yes :tangle webdriver-impl.scm :exports none
|
||||
(import r7rs
|
||||
(chicken base)
|
||||
(chicken string)
|
||||
(chicken process)
|
||||
(chicken gc)
|
||||
<<dependencies-for-imports()>>
|
||||
)
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle tests/run.scm :exports none :mkdirp yes
|
||||
(include-relative "../webdriver-impl.scm")
|
||||
#+end_src
|
||||
|
||||
* Error Conditions
|
||||
|
||||
#+name: wd-exception
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-condition-type &wd-exception &error wd-exception?
|
||||
(stacktrace wd-stacktrace)
|
||||
(data wd-data))
|
||||
#+end_src
|
||||
|
||||
Every API error code (key "error" in the returned JSON data) gets its own condition type, prefixed by ~&~. They all inherit from ~&wd-exception~.
|
||||
|
||||
#+name: error-code-table
|
||||
| Name | API Error Code |
|
||||
|---------------------------+---------------------------|
|
||||
| detached-shadow-root | detached shadow root |
|
||||
| element-click-intercepted | element click intercepted |
|
||||
| element-not-interactable | element not interactable |
|
||||
| insecure-certificate | insecure certificate |
|
||||
| invalid-argument | invalid argument |
|
||||
| invalid-cookie-domain | invalid cookie domain |
|
||||
| invalid-element-state | invalid element state |
|
||||
| invalid-selector | invalid selector |
|
||||
| invalid-session-id | invalid session id |
|
||||
| javascript-error | javascript error |
|
||||
| move-target-out-of-bounds | move target out of bounds |
|
||||
| no-such-alert | no such alert |
|
||||
| no-such-cookie | no such cookie |
|
||||
| no-such-element | no such element |
|
||||
| no-such-frame | no such frame |
|
||||
| no-such-shadow-root | no such shadow root |
|
||||
| no-such-window | no such window |
|
||||
| script-timeout | script timeout |
|
||||
| session-not-created | session not created |
|
||||
| stale-element-reference | stale element reference |
|
||||
| timeout | timeout |
|
||||
| unable-to-capture-screen | unable to capture screen |
|
||||
| unable-to-set-cookie | unable to set cookie |
|
||||
| unexpected-alert-open | unexpected alert open |
|
||||
| unknown-command | unknown command |
|
||||
| unknown-error | unknown error |
|
||||
| unknown-method | unknown method |
|
||||
| unsupported-operation | unsupported operation |
|
||||
|
||||
#+name: define-conditions
|
||||
#+begin_src emacs-lisp :var src=error-code-table :exports none :results raw
|
||||
(mapconcat
|
||||
(lambda (row)
|
||||
(let ((replace `((?n . ,(cl-first row))
|
||||
(?c . ,(cl-second row)))))
|
||||
(format-spec "(define-condition-type &%n &wd-exception %n?)" replace)))
|
||||
src "\n")
|
||||
#+end_src
|
||||
|
||||
#+name: define-condition-thrower
|
||||
#+begin_src emacs-lisp :var src=error-code-table :exports none :results raw
|
||||
(concat "(define (wd-throw data)\n"
|
||||
" (case (alist-ref data 'error)\n"
|
||||
(mapconcat
|
||||
(lambda (row)
|
||||
(let ((replace `((?n . ,(cl-first row))
|
||||
(?c . ,(cl-second row)))))
|
||||
(format-spec " ((\"%c\") (raise (make-condition &%n (alist-ref data 'stacktrace) data)))" replace)))
|
||||
src "\n")
|
||||
"\n"
|
||||
" (else (raise (make-condition &wd-exception (alist-ref data 'stacktrace) data)))\n"
|
||||
" )\n"
|
||||
")")
|
||||
#+end_src
|
||||
|
||||
#+name: conditions
|
||||
#+begin_src scheme :tangle webdriver-impl.scm :noweb yes :exports none
|
||||
<<define-conditions()>>
|
||||
|
||||
<<define-condition-thrower()>>
|
||||
#+end_src
|
||||
|
||||
* WebDriver
|
||||
|
||||
The core element of the library is the ~<WebDriver>~ class and its subclasses. The class has the following fields:
|
||||
|
||||
#+name: webdriver-class
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-class <WebDriver> ()
|
||||
((browser #f)
|
||||
(active? #f)
|
||||
(browser-pid #f)
|
||||
(server #f)
|
||||
(port #f)
|
||||
(session-id #f)
|
||||
(prefs #f)
|
||||
(capabilities #f)))
|
||||
#+end_src
|
||||
|
||||
The parent class provides a handful of methods, but does not implement all of them; some are the sole responsibility of the subclass. The ~launch~ method, on the other hand, bears shared responsibility. It sets a finalizer to ensure termination of the web driver process in case the class is disposed of with a still-open driver.
|
||||
|
||||
#+name: webdriver-basics
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (launch #:after (instance <WebDriver>) options)
|
||||
(set-finalizer! instance (lambda (obj)
|
||||
(when (slot-value instance 'active?)
|
||||
(terminate instance)))))
|
||||
|
||||
(define-method (terminate (instance <WebDriver>))
|
||||
(terminate-session instance)
|
||||
(process-signal (slot-value instance 'browser-pid))
|
||||
(set! (slot-value instance 'browser-pid) #f)
|
||||
(set! (slot-value instance 'active?) #f))
|
||||
|
||||
(define-method (construct-capabilities (instance <WebDriver>) #!optional caps)
|
||||
(raise 'subclass-responsibility))
|
||||
|
||||
(define-method (postprocess-result (instance <WebDriver>) result)
|
||||
result)
|
||||
#+end_src
|
||||
|
||||
Main initialization is done by calling the ~new-WebDriver~ procedure with the respective class name and optionally an alist of options.
|
||||
|
||||
#+name: webdriver-init
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define (new-WebDriver browser #!optional options)
|
||||
(let ((instance (make browser)))
|
||||
(launch instance options)
|
||||
(sleep 1)
|
||||
instance))
|
||||
#+end_src
|
||||
|
||||
** Geckodriver
|
||||
|
||||
The Geckodriver is used to control Firefox.
|
||||
|
||||
#+name: geckodriver-basic
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-class <Gecko> (<WebDriver>)
|
||||
((browser #:firefox)
|
||||
(server "127.0.0.1")
|
||||
(port 4444)))
|
||||
|
||||
(define-method (launch (instance <Gecko>) options)
|
||||
(let ((pid (process-run "geckodriver > /dev/null 2>&1")))
|
||||
(set! (slot-value instance 'browser-pid) pid)
|
||||
(set! (slot-value instance 'active?) #t)
|
||||
(set! (slot-value instance 'capabilities) options)))
|
||||
#+end_src
|
||||
|
||||
The capabilities object for Geckodriver is of the form ={"capabilities": {...}}=.
|
||||
For more information on capabilities, see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities.
|
||||
|
||||
#+name: geckodriver-capabilities
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (construct-capabilities (instance <Gecko>))
|
||||
(let ((caps (or (slot-value instance 'capabilities) (list))))
|
||||
`((capabilities . ,caps))))
|
||||
#+end_src
|
||||
|
||||
Sometimes, Geckodriver returns the results of a command in a JSON object with the sole key ="value"=. We have to correct that before returning the data to the user.
|
||||
|
||||
#+name: geckodriver-postprocess
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (postprocess-result (instance <Gecko>) result)
|
||||
(alist-ref/default result 'value result))
|
||||
#+end_src
|
||||
|
||||
#+name: prep-geckodriver-test
|
||||
#+begin_src scheme :tangle tests/run.scm :noweb strip-tangle :exports none :post test-post(input=*this*) :results output
|
||||
<<prep-test>>
|
||||
<<wd-exception>>
|
||||
<<conditions>>
|
||||
<<webdriver-class>>
|
||||
<<webdriver-basics>>
|
||||
<<webdriver-init>>
|
||||
<<geckodriver-basic>>
|
||||
<<geckodriver-capabilities>>
|
||||
<<geckodriver-postprocess>>
|
||||
<<wd-send>>
|
||||
<<wd-init-session>>
|
||||
<<wd-term-session>>
|
||||
#+end_src
|
||||
|
||||
* WebDriver API
|
||||
|
||||
** Communication
|
||||
|
||||
Data is sent to the API via a central class method. For convenience, there is a ~send-with-session~ variant that automatically adds the session id.
|
||||
|
||||
#+name: wd-send
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (send (instance <WebDriver>) data uri method)
|
||||
(let* ((remote (string-append "http://" (slot-value instance 'server) ":" (->string (slot-value instance 'port)) "/"))
|
||||
(result (postprocess-result instance
|
||||
(with-input-from-request
|
||||
(make-request #:method method
|
||||
#:uri (uri-reference (string-append remote uri))
|
||||
#:headers (headers `((content-type application/json))))
|
||||
(if data (json->string data) "")
|
||||
read-json))))
|
||||
(if (and (list? result) (alist-ref/default result 'error #f))
|
||||
(wd-throw result)
|
||||
result)))
|
||||
|
||||
(define-method (send-with-session (instance <WebDriver>) data uri method)
|
||||
(send instance data (string-append "session/" (slot-value instance 'session-id) "/" uri) method))
|
||||
#+end_src
|
||||
|
||||
** Session management
|
||||
|
||||
Session management is very simple. There is just one method to initialize a new session. Everything else is handled automatically.
|
||||
|
||||
#+name: wd-init-session
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (initialize-session (instance <WebDriver>))
|
||||
(let ((result (send instance (construct-capabilities instance) "session" 'POST)))
|
||||
(set! (slot-value instance 'session-id) (alist-ref result 'sessionId))))
|
||||
#+end_src
|
||||
|
||||
#+name: wd-term-session
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (terminate-session (instance <WebDriver>))
|
||||
(when (slot-value instance 'session-id)
|
||||
(send instance #f (string-append "session/" (slot-value instance 'session-id)) 'DELETE))
|
||||
(set! (slot-value instance 'session-id) #f))
|
||||
#+end_src
|
||||
|
||||
#+name: wd-session-test
|
||||
#+begin_src scheme :tangle tests/run.scm :noweb strip-tangle :exports results :post test-post(input=*this*) :results output
|
||||
<<prep-geckodriver-test>>
|
||||
(test-group "session"
|
||||
(let ((browser (new-WebDriver <Gecko>)))
|
||||
(test "Initial state" #f (slot-value browser 'session-id))
|
||||
(test-assert "Session id check" (string? (begin (initialize-session browser) (slot-value browser 'session-id))))
|
||||
(test-assert "Session id after termination" (eq? #f (begin (terminate-session browser) (slot-value browser 'session-id))))
|
||||
(terminate browser)))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: wd-session-test
|
||||
: -- testing session -----------------------------------------------------------
|
||||
: Initial state ........................................................ [ PASS]
|
||||
: Session id check ..................................................... [ PASS]
|
||||
: Session id after termination ......................................... [ PASS]
|
||||
: 3 tests completed in 3.788 seconds.
|
||||
: 3 out of 3 (100%) tests passed.
|
||||
: -- done testing session ------------------------------------------------------
|
||||
|
||||
** API Access Methods
|
||||
|
||||
#+name: wd-url
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (set-url (instance <WebDriver>) url)
|
||||
(send-with-session instance `((url . ,url)) "url" 'POST))
|
||||
|
||||
(define-method (url (instance <WebDriver>))
|
||||
(send-with-session instance #f "url" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+name: wd-url-test
|
||||
#+begin_src scheme :tangle tests/run.scm :noweb strip-tangle :exports results :post test-post(input=*this*) :results output
|
||||
<<prep-geckodriver-test>>
|
||||
<<wd-url>>
|
||||
(test-group "url"
|
||||
(let ((browser (new-WebDriver <Gecko>)))
|
||||
(test "Initial state" #f (slot-value browser 'session-id))
|
||||
(test "Navigating to the first website" "http://info.cern.ch/hypertext/WWW/TheProject.html"
|
||||
(begin (initialize-session browser)
|
||||
(set-url browser "http://info.cern.ch/hypertext/WWW/TheProject.html")
|
||||
(url browser)))
|
||||
(terminate browser)))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: wd-url-test
|
||||
: -- testing url ---------------------------------------------------------------
|
||||
: Initial state ........................................................ [ PASS]
|
||||
: Navigating to the first website ...................................... [ PASS]
|
||||
: 2 tests completed in 5.247 seconds.
|
||||
: 2 out of 2 (100%) tests passed.
|
||||
: -- done testing url ----------------------------------------------------------
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (back (instance <WebDriver>))
|
||||
(send-with-session instance #f "back" 'POST))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (forward (instance <WebDriver>))
|
||||
(send-with-session instance #f "forward" 'POST))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (refresh (instance <WebDriver>))
|
||||
(send-with-session instance #f "refresh" 'POST))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (title (instance <WebDriver>))
|
||||
(send-with-session instance #f "title" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (status (instance <WebDriver>))
|
||||
(send-with-session instance #f "status" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (source (instance <WebDriver>))
|
||||
(send-with-session instance #f "source" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (screenshot (instance <WebDriver>))
|
||||
(base64-decode (send-with-session instance #f "screenshot" 'GET)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (print-page (instance <WebDriver>))
|
||||
(send-with-session instance #f "print" 'POST))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (execute-async (instance <WebDriver>) script args)
|
||||
(send-with-session instance `((script . ,script) (args . ,args)) "execute/async" 'POST))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (execute-sync (instance <WebDriver>) script args)
|
||||
(send-with-session instance `((script . ,script) (args . ,args)) "execute/sync" 'POST))
|
||||
#+end_src
|
||||
|
||||
** Timeouts
|
||||
|
||||
The following timeouts are defined:
|
||||
|
||||
- =script=: defaults to 30'000, specifies when to interrupt a script that is being evaluated. A nil value implies that scripts should never be interrupted, but instead run indefinitely.
|
||||
- =pageLoad=: defaults to 300'000, provides the timeout limit used to interrupt an explicit navigation attempt.
|
||||
- =implicit=: defaults to 0, specifies a time to wait for the element location strategy to complete when locating an element.
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-class <WDTimeouts> ()
|
||||
((script 30000)
|
||||
(pageLoad 300000)
|
||||
(implicit 0)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (extract (instance <WDTimeouts>))
|
||||
`((script . ,(slot-value instance 'script))
|
||||
(pageLoad . ,(slot-value instance 'pageLoad))
|
||||
(implicit . ,(slot-value instance 'implicit))))
|
||||
#+end_src
|
||||
|
||||
*** Setting and getting timeouts
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (set-timeouts (instance <WebDriver>) (timeouts <WDTimeouts>))
|
||||
(send-with-session instance (extract timeouts) "timeouts" 'POST))
|
||||
|
||||
(define-method (timeouts (instance <WebDriver>))
|
||||
(let ((result (send-with-session instance #f "timeouts" 'GET)))
|
||||
(make <WDTimeouts>
|
||||
'script (alist-ref result 'script)
|
||||
'pageLoad (alist-ref result 'pageLoad)
|
||||
'implicit (alist-ref result 'implicit))))
|
||||
#+end_src
|
||||
|
||||
** Elements
|
||||
|
||||
*** Element Class
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-class <WDElement> ()
|
||||
((driver #f)
|
||||
(element #f)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (send-with-session (instance <WDElement>) data uri method)
|
||||
(send-with-session (slot-value instance 'driver) data
|
||||
(string-append "element/" (slot-value instance 'element) "/" uri)
|
||||
method))
|
||||
#+end_src
|
||||
|
||||
*** Finding Elements
|
||||
|
||||
**** Location Strategies
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define css-selector "css selector")
|
||||
(define link-text "link text")
|
||||
(define partial-link-text "partial link text")
|
||||
(define tag-name "tag name")
|
||||
(define xpath "xpath")
|
||||
#+end_src
|
||||
|
||||
**** Accessor Methods
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (find-element (instance <WebDriver>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "element" 'POST)))
|
||||
(make <WDElement> 'driver instance 'element (car (alist-values result)))
|
||||
element))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (find-elements (instance <WebDriver>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "elements" 'POST)))
|
||||
(map
|
||||
(lambda (elem)
|
||||
(make <WDElement> 'driver instance 'element (car (alist-values elem))))
|
||||
result)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (find-element (instance <WDElement>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "element" 'POST)))
|
||||
(make <WDElement> 'driver (slot-value instance 'driver) 'element (car (alist-values result)))
|
||||
element))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (find-elements (instance <WDElement>) strategy selector)
|
||||
(let ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) "elements" 'POST)))
|
||||
(map
|
||||
(lambda (elem)
|
||||
(make <WDElement> 'driver (slot-value instance 'driver) 'element (car (alist-values elem))))
|
||||
result)))
|
||||
#+end_src
|
||||
|
||||
*** Working with Elements
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (attribute (instance <WDElement>) attribute)
|
||||
(let ((result (send-with-session instance #f
|
||||
(string-append "attribute/" attribute)
|
||||
'GET)))
|
||||
(if (equal? "true" result)
|
||||
#t
|
||||
result)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (property (instance <WDElement>) property)
|
||||
(send-with-session instance #f (string-append "property/" property) 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (clear (instance <WDElement>))
|
||||
(send-with-session instance #f "clear" 'POST))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (click (instance <WDElement>))
|
||||
(send-with-session instance #f "click" 'POST))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (computed-label (instance <WDElement>))
|
||||
(send-with-session instance #f "computedlabel" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (computed-role (instance <WDElement>))
|
||||
(send-with-session instance #f "computedrole" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (enabled? (instance <WDElement>))
|
||||
(send-with-session instance #f "enabled" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (selected? (instance <WDElement>))
|
||||
(send-with-session instance #f "selected" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (name (instance <WDElement>))
|
||||
(send-with-session instance #f "name" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (rect (instance <WDElement>))
|
||||
(send-with-session instance #f "rect" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (screenshot (instance <WDElement>))
|
||||
(base64-decode (send-with-session instance #f "screenshot" 'GET)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (text (instance <WDElement>))
|
||||
(send-with-session instance #f "text" 'GET))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle webdriver-impl.scm
|
||||
(define-method (set-value (instance <WDElement>) value)
|
||||
(send-with-session instance `((text . ,value)) "value" 'POST))
|
||||
#+end_src
|
||||
|
||||
* About This Egg
|
||||
|
||||
#+begin_src scheme :noweb yes :tangle webdriver.egg :exports none
|
||||
;; -*- scheme -*-
|
||||
((author "Daniel Ziltener")
|
||||
(synopsis "A WebDriver API implementation for Chicken")
|
||||
(category web)
|
||||
(license "BSD")
|
||||
(version <<latest-release()>>)
|
||||
(dependencies r7rs <<dependencies-for-egg()>>)
|
||||
(test-dependencies test)
|
||||
|
||||
(components
|
||||
(extension webdriver
|
||||
(csc-options "-X" "r7rs" "-R" "r7rs" "-sJ"))))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :tangle tests/run.scm :exports none
|
||||
(test-exit)
|
||||
#+end_src
|
||||
|
||||
** Source
|
||||
|
||||
The source is available at [[https://fossil.lyrion.ch/chicken-webdriver]].
|
||||
|
||||
** Author
|
||||
|
||||
Daniel Ziltener
|
||||
|
||||
** Version History
|
||||
|
||||
#+name: version-history
|
||||
| 0.5 | Initial Release |
|
||||
|
||||
#+name: gen-releases
|
||||
#+begin_src emacs-lisp :var vers=version-history :results raw :exports none
|
||||
(mapconcat (lambda (row) (concat "(release \"" (number-to-string (car row)) "\") ;; " (cadr row)))
|
||||
vers "\n")
|
||||
#+end_src
|
||||
|
||||
#+name: latest-release
|
||||
#+begin_src emacs-lisp :var vers=version-history :exports none :results code
|
||||
(number-to-string (caar vers))
|
||||
#+end_src
|
||||
|
||||
#+begin_src scheme :noweb yes :tangle webdriver.release-info :exports none
|
||||
;; -*- scheme -*-
|
||||
(repo fossil "https://fossil.lyrion.ch/chicken-webdriver")
|
||||
(uri targz "https://fossil.lyrion.ch/chicken-webdriver")
|
||||
<<gen-releases()>>
|
||||
#+end_src
|
||||
|
||||
** License
|
||||
|
||||
#+begin_src fundamental :tangle LICENSE
|
||||
Copyright (C) 2023 Daniel Ziltener
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#+end_src
|
6
webdriver.release-info
Normal file
6
webdriver.release-info
Normal file
|
@ -0,0 +1,6 @@
|
|||
;; [[file:webdriver.org::*Version History / Repository][Version History / Repository:3]]
|
||||
;; -*- scheme -*-
|
||||
(repo fossil "https://fossil.lyrion.ch/chicken-webdriver")
|
||||
(uri targz "https://fossil.lyrion.ch/chicken-webdriver")
|
||||
(release "0.5") ;; Initial Release
|
||||
;; Version History / Repository:3 ends here
|
7
webdriver.scm
Normal file
7
webdriver.scm
Normal file
|
@ -0,0 +1,7 @@
|
|||
;; [[file:webdriver.org::*Dependencies][Dependencies:3]]
|
||||
(import r7rs)
|
||||
(define-library (webdriver)
|
||||
(import (scheme base))
|
||||
(export <WDElement>)
|
||||
(include "webdriver-impl.scm"))
|
||||
;; Dependencies:3 ends here
|
Loading…
Reference in a new issue