raft/webdriver.html

1032 lines
44 KiB
HTML
Raw Normal View History

<div class='fossil-doc' data-title='Webdriver implementation in Chicken Scheme'>
.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="#orgff39d75">1. Dependencies</a></li>
<li><a href="#org4651c2d">2. Error Conditions</a></li>
<li><a href="#org6193099">3. WebDriver</a>
<ul>
<li><a href="#org4f8bad8">3.1. Geckodriver</a></li>
</ul>
</li>
<li><a href="#org4680eae">4. WebDriver API</a>
<ul>
<li><a href="#orgb60d0eb">4.1. Communication</a></li>
<li><a href="#org2a85680">4.2. Session management</a></li>
<li><a href="#org50c43d3">4.3. API Access Methods</a></li>
<li><a href="#org970cd9c">4.4. Timeouts</a>
<ul>
<li><a href="#orgc089ded">4.4.1. Setting and getting timeouts</a></li>
</ul>
</li>
<li><a href="#org8473447">4.5. Elements</a>
<ul>
<li><a href="#org870ddb2">4.5.1. Element Class</a></li>
<li><a href="#org3abcade">4.5.2. Finding Elements</a></li>
<li><a href="#org1ee7ac0">4.5.3. Working with Elements</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#org86b4a66">5. About This Egg</a>
<ul>
<li><a href="#orgac72f6e">5.1. Source</a></li>
<li><a href="#orgd19a870">5.2. Author</a></li>
<li><a href="#orgc04d23c">5.3. Version History</a></li>
<li><a href="#org9889d78">5.4. License</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-orgff39d75" class="outline-2">
<h2 id="orgff39d75"><span class="section-number-2">1.</span> Dependencies</h2>
<div class="outline-text-2" id="text-1">
<table id="org541a8e9" 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-org4651c2d" class="outline-2">
<h2 id="org4651c2d"><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="orgbc037c3">(define-condition-type &amp;wd-exception &amp;error wd-exception?
(stacktrace wd-stacktrace)
(data wd-data))
</pre>
</div>
<p>
Every API error code (key "error" in the returned JSON data) gets its own condition type, prefixed by <code>&amp;</code>. They all inherit from <code>&amp;wd-exception</code>.
</p>
<table id="orgecaa72e" 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-org6193099" class="outline-2">
<h2 id="org6193099"><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>&lt;WebDriver&gt;</code> class and its subclasses. The class has the following fields:
</p>
<div class="org-src-container">
<pre class="src src-scheme" id="org44a4d03">(<span style="font-weight: bold;">define-class</span> <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span> ()
((browser #f)
(active? #f)
(browser-pid #f)
(server #f)
(port #f)
(session-id #f)
(prefs #f)
(capabilities #f)))
</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="orgf8c4322">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">launch</span> <span style="font-weight: bold;">#:after</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) options)
(set-finalizer! instance (<span style="font-weight: bold;">lambda</span> (obj)
(<span style="font-weight: bold;">when</span> (slot-value instance 'active?)
(terminate instance)))))
(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">terminate</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(terminate-session instance)
(process-signal (slot-value instance 'browser-pid))
(set! (slot-value instance 'browser-pid) #f)
(set! (slot-value instance 'active?) #f))
(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">construct-capabilities</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) #!optional caps)
(raise 'subclass-responsibility))
(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">postprocess-result</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) result)
result)
</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="org6bc1a19">(<span style="font-weight: bold;">define</span> (<span style="font-weight: bold;">new-WebDriver</span> browser #!optional options)
(<span style="font-weight: bold;">let</span> ((instance (make browser)))
(launch instance options)
(sleep 1)
instance))
</pre>
</div>
</div>
<div id="outline-container-org4f8bad8" class="outline-3">
<h3 id="org4f8bad8"><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="orgd7e1d00">(<span style="font-weight: bold;">define-class</span> <span style="font-weight: bold; text-decoration: underline;">&lt;Gecko&gt;</span> (<span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>)
((browser <span style="font-weight: bold;">#:firefox</span>)
(server <span style="font-style: italic;">"127.0.0.1"</span>)
(port 4444)))
(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">launch</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;Gecko&gt;</span>) options)
(<span style="font-weight: bold;">let</span> ((pid (process-run <span style="font-style: italic;">"geckodriver &gt; /dev/null 2&gt;&amp;1"</span>)))
(set! (slot-value instance 'browser-pid) pid)
(set! (slot-value instance 'active?) #t)
(set! (slot-value instance 'capabilities) options)))
</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="org5a3ee36">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">construct-capabilities</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;Gecko&gt;</span>))
(<span style="font-weight: bold;">let</span> ((caps (<span style="font-weight: bold;">or</span> (slot-value instance 'capabilities) (list))))
`((capabilities . ,caps))))
</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="org6ddb4c4">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">postprocess-result</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;Gecko&gt;</span>) result)
(alist-ref/default result 'value result))
</pre>
</div>
</div>
</div>
</div>
<div id="outline-container-org4680eae" class="outline-2">
<h2 id="org4680eae"><span class="section-number-2">4.</span> WebDriver API</h2>
<div class="outline-text-2" id="text-4">
</div>
<div id="outline-container-orgb60d0eb" class="outline-3">
<h3 id="orgb60d0eb"><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="org13f277a">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">send</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) data uri method)
(<span style="font-weight: bold;">let*</span> ((remote (string-append <span style="font-style: italic;">"http://"</span> (slot-value instance 'server) <span style="font-style: italic;">":"</span> (-&gt;string (slot-value instance 'port)) <span style="font-style: italic;">"/"</span>))
(result (postprocess-result instance
(with-input-from-request
(make-request <span style="font-weight: bold;">#:method</span> method
<span style="font-weight: bold;">#:uri</span> (uri-reference (string-append remote uri))
<span style="font-weight: bold;">#:headers</span> (headers `((content-type application/json))))
(<span style="font-weight: bold;">if</span> data (json-&gt;string data) <span style="font-style: italic;">""</span>)
read-json))))
(<span style="font-weight: bold;">if</span> (<span style="font-weight: bold;">and</span> (list? result) (alist-ref/default result 'error #f))
(wd-throw result)
result)))
(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">send-with-session</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) data uri method)
(send instance data (string-append <span style="font-style: italic;">"session/"</span> (slot-value instance 'session-id) <span style="font-style: italic;">"/"</span> uri) method))
</pre>
</div>
</div>
</div>
<div id="outline-container-org2a85680" class="outline-3">
<h3 id="org2a85680"><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="org31bcb1b">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">initialize-session</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(<span style="font-weight: bold;">let</span> ((result (send instance (construct-capabilities instance) <span style="font-style: italic;">"session"</span> 'POST)))
(set! (slot-value instance 'session-id) (alist-ref result 'sessionId))))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme" id="orgf32e94f">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">terminate-session</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(<span style="font-weight: bold;">when</span> (slot-value instance 'session-id)
(send instance #f (string-append <span style="font-style: italic;">"session/"</span> (slot-value instance 'session-id)) 'DELETE))
(set! (slot-value instance 'session-id) #f))
</pre>
</div>
<pre class="example">
-- 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 ------------------------------------------------------
</pre>
</div>
</div>
<div id="outline-container-org50c43d3" class="outline-3">
<h3 id="org50c43d3"><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="org9ff460f">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">set-url</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) url)
(send-with-session instance `((url . ,url)) <span style="font-style: italic;">"url"</span> 'POST))
(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">url</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"url"</span> 'GET))
</pre>
</div>
<pre class="example">
-- 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 ----------------------------------------------------------
</pre>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">back</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"back"</span> 'POST))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">forward</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"forward"</span> 'POST))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">refresh</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"refresh"</span> 'POST))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">title</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"title"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">status</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"status"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">source</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"source"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">screenshot</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(base64-decode (send-with-session instance #f <span style="font-style: italic;">"screenshot"</span> 'GET)))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">print-page</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"print"</span> 'POST))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">execute-async</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) script args)
(send-with-session instance `((script . ,script) (args . ,args)) <span style="font-style: italic;">"execute/async"</span> 'POST))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">execute-sync</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) script args)
(send-with-session instance `((script . ,script) (args . ,args)) <span style="font-style: italic;">"execute/sync"</span> 'POST))
</pre>
</div>
</div>
</div>
<div id="outline-container-org970cd9c" class="outline-3">
<h3 id="org970cd9c"><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="font-weight: bold;">define-class</span> <span style="font-weight: bold; text-decoration: underline;">&lt;WDTimeouts&gt;</span> ()
((script 30000)
(pageLoad 300000)
(implicit 0)))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">extract</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDTimeouts&gt;</span>))
`((script . ,(slot-value instance 'script))
(pageLoad . ,(slot-value instance 'pageLoad))
(implicit . ,(slot-value instance 'implicit))))
</pre>
</div>
</div>
<div id="outline-container-orgc089ded" class="outline-4">
<h4 id="orgc089ded"><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="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">set-timeouts</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) (timeouts <span style="font-weight: bold; text-decoration: underline;">&lt;WDTimeouts&gt;</span>))
(send-with-session instance (extract timeouts) <span style="font-style: italic;">"timeouts"</span> 'POST))
(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">timeouts</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>))
(<span style="font-weight: bold;">let</span> ((result (send-with-session instance #f <span style="font-style: italic;">"timeouts"</span> 'GET)))
(make <span style="font-weight: bold; text-decoration: underline;">&lt;WDTimeouts&gt;</span>
'script (alist-ref result 'script)
'pageLoad (alist-ref result 'pageLoad)
'implicit (alist-ref result 'implicit))))
</pre>
</div>
</div>
</div>
</div>
<div id="outline-container-org8473447" class="outline-3">
<h3 id="org8473447"><span class="section-number-3">4.5.</span> Elements</h3>
<div class="outline-text-3" id="text-4-5">
</div>
<div id="outline-container-org870ddb2" class="outline-4">
<h4 id="org870ddb2"><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="font-weight: bold;">define-class</span> <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span> ()
((driver #f)
(element #f)))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">send-with-session</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>) data uri method)
(send-with-session (slot-value instance 'driver) data
(string-append <span style="font-style: italic;">"element/"</span> (slot-value instance 'element) <span style="font-style: italic;">"/"</span> uri)
method))
</pre>
</div>
</div>
</div>
<div id="outline-container-org3abcade" class="outline-4">
<h4 id="org3abcade"><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="org671c47f"></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="font-weight: bold;">define</span> <span style="font-weight: bold;">css-selector</span> <span style="font-style: italic;">"css selector"</span>)
(<span style="font-weight: bold;">define</span> <span style="font-weight: bold;">link-text</span> <span style="font-style: italic;">"link text"</span>)
(<span style="font-weight: bold;">define</span> <span style="font-weight: bold;">partial-link-text</span> <span style="font-style: italic;">"partial link text"</span>)
(<span style="font-weight: bold;">define</span> <span style="font-weight: bold;">tag-name</span> <span style="font-style: italic;">"tag name"</span>)
(<span style="font-weight: bold;">define</span> <span style="font-weight: bold;">xpath</span> <span style="font-style: italic;">"xpath"</span>)
</pre>
</div>
</div>
</li>
<li><a id="orgd2e56a5"></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="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">find-element</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) strategy selector)
(<span style="font-weight: bold;">let</span> ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) <span style="font-style: italic;">"element"</span> 'POST)))
(make <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span> 'driver instance 'element (car (alist-values result)))
element))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">find-elements</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WebDriver&gt;</span>) strategy selector)
(<span style="font-weight: bold;">let</span> ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) <span style="font-style: italic;">"elements"</span> 'POST)))
(<span style="font-weight: bold;">map</span>
(<span style="font-weight: bold;">lambda</span> (elem)
(make <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span> 'driver instance 'element (car (alist-values elem))))
result)))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">find-element</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>) strategy selector)
(<span style="font-weight: bold;">let</span> ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) <span style="font-style: italic;">"element"</span> 'POST)))
(make <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span> 'driver (slot-value instance 'driver) 'element (car (alist-values result)))
element))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">find-elements</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>) strategy selector)
(<span style="font-weight: bold;">let</span> ((result (send-with-session instance `((using . ,strategy) (value . ,selector)) <span style="font-style: italic;">"elements"</span> 'POST)))
(<span style="font-weight: bold;">map</span>
(<span style="font-weight: bold;">lambda</span> (elem)
(make <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span> 'driver (slot-value instance 'driver) 'element (car (alist-values elem))))
result)))
</pre>
</div>
</div>
</li>
</ol>
</div>
<div id="outline-container-org1ee7ac0" class="outline-4">
<h4 id="org1ee7ac0"><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="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">attribute</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>) attribute)
(<span style="font-weight: bold;">let</span> ((result (send-with-session instance #f
(string-append <span style="font-style: italic;">"attribute/"</span> attribute)
'GET)))
(<span style="font-weight: bold;">if</span> (equal? <span style="font-style: italic;">"true"</span> result)
#t
result)))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">property</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>) property)
(send-with-session instance #f (string-append <span style="font-style: italic;">"property/"</span> property) 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">clear</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"clear"</span> 'POST))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">click</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"click"</span> 'POST))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">computed-label</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"computedlabel"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">computed-role</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"computedrole"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">enabled?</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"enabled"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">selected?</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"selected"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">name</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"name"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">rect</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"rect"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">screenshot</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(base64-decode (send-with-session instance #f <span style="font-style: italic;">"screenshot"</span> 'GET)))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">text</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>))
(send-with-session instance #f <span style="font-style: italic;">"text"</span> 'GET))
</pre>
</div>
<div class="org-src-container">
<pre class="src src-scheme">(<span style="font-weight: bold;">define-method</span> (<span style="font-weight: bold;">set-value</span> (instance <span style="font-weight: bold; text-decoration: underline;">&lt;WDElement&gt;</span>) value)
(send-with-session instance `((text . ,value)) <span style="font-style: italic;">"value"</span> 'POST))
</pre>
</div>
</div>
</div>
</div>
</div>
<div id="outline-container-org86b4a66" class="outline-2">
<h2 id="org86b4a66"><span class="section-number-2">5.</span> About This Egg</h2>
<div class="outline-text-2" id="text-5">
</div>
<div id="outline-container-orgac72f6e" class="outline-3">
<h3 id="orgac72f6e"><span class="section-number-3">5.1.</span> Source</h3>
<div class="outline-text-3" id="text-5-1">
<p>
The source is available at <a href="https://fossil.lyrion.ch/chicken-webdriver">https://fossil.lyrion.ch/chicken-webdriver</a>.
</p>
</div>
</div>
<div id="outline-container-orgd19a870" class="outline-3">
<h3 id="orgd19a870"><span class="section-number-3">5.2.</span> Author</h3>
<div class="outline-text-3" id="text-5-2">
<p>
Daniel Ziltener
</p>
</div>
</div>
<div id="outline-container-orgc04d23c" class="outline-3">
<h3 id="orgc04d23c"><span class="section-number-3">5.3.</span> Version History</h3>
<div class="outline-text-3" id="text-5-3">
<table id="org3b5abd1" border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-right" />
<col class="org-left" />
</colgroup>
<tbody>
<tr>
<td class="org-right">0.5</td>
<td class="org-left">Initial Release</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="outline-container-org9889d78" class="outline-3">
<h3 id="org9889d78"><span class="section-number-3">5.4.</span> License</h3>
<div class="outline-text-3" id="text-5-4">
<div class="org-src-container">
<pre class="src src-fundamental">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 &lt;organization&gt; 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 &lt;COPYRIGHT HOLDER&gt; 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.
</pre>
</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:54</p>
<p class="validation"><a href="https://validator.w3.org/check?uri=referer">Validate</a></p>
</div>
</div>