├── test ├── test.txt ├── chrome-bug.scm~ └── driver.scm ├── .gitignore ├── makefile ├── install.sh ├── TODO ├── web ├── driver │ └── key.scm └── driver.scm ├── readme.md └── LICENSE /test/test.txt: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tested 2 | libs 3 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | default: .tested 2 | 3 | GUILE ?= guile 4 | 5 | .tested: web/*.scm test/*.scm 6 | hdt 7 | touch $@ 8 | 9 | clean: 10 | rm -rf .tested 11 | 12 | GUILE_SITE_DIR ?= $(shell $(GUILE) -c "(display (%site-dir)) (newline)") 13 | 14 | install: 15 | install -D --target-directory=$(GUILE_SITE_DIR)/web web/*.scm 16 | install -D --target-directory=$(GUILE_SITE_DIR)/web/driver web/driver/*.scm 17 | 18 | uninstall: 19 | rm -rf $(GUILE_SITE_DIR)/web/driver 20 | rm -rf $(GUILE_SITE_DIR)/web/driver.scm 21 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | 6 | export GUILE=guile 7 | 8 | if pacman --version; then 9 | sudo pacman -Syu --needed wget atool guile chromium rust firefox 10 | fi 11 | if dnf --version; then 12 | sudo dnf install wget atool guile22 chromium chromedriver cargo firefox 13 | export GUILE=guile2.2 14 | echo *** To use guile-web-driver with fedora, use command guile2.2 instead of guile! *** 15 | fi 16 | mkdir -p libs 17 | GUILE_JSON_VERSION=4.7.3 18 | GECKODRIVER_VERSION=0.30.0 19 | cd libs 20 | wget https://download.savannah.gnu.org/releases/guile-json/guile-json-$GUILE_JSON_VERSION.tar.gz 21 | aunpack guile-json-$GUILE_JSON_VERSION.tar.gz 22 | cd guile-json-$GUILE_JSON_VERSION 23 | ./configure --prefix=/usr 24 | make 25 | sudo make install 26 | cd .. 27 | wget https://github.com/mozilla/geckodriver/archive/refs/tags/v$GECKODRIVER_VERSION.tar.gz \ 28 | --output-document=geckodriver-$GECKODRIVER_VERSION.tar.gz 29 | aunpack geckodriver-$GECKODRIVER_VERSION.tar.gz 30 | cd geckodriver-$GECKODRIVER_VERSION 31 | cargo install --path . 32 | cd .. 33 | export PATH=$PATH:~/.cargo/bin 34 | echo *** Make sure ~/.cargo/bin is in your path to use geckodriver! *** 35 | cd .. 36 | 37 | make clean 38 | make 39 | sudo make GUILE=$GUILE install 40 | 41 | -------------------------------------------------------------------------------- /test/chrome-bug.scm~: -------------------------------------------------------------------------------- 1 | (define-module (test chrome-bug)) 2 | 3 | (use-modules 4 | (hdt hdt) 5 | (ice-9 match) 6 | (web driver) (web uri) (web request)) 7 | 8 | (test chrome-bug 9 | (set-web-handler! 10 | (lambda (request body) 11 | (match (uri-path (request-uri request)) 12 | ("/a.html" 13 | (values 14 | '((content-type . (text/html))) 15 | " 16 |
17 | b 18 | ")) 19 | ("/b.html" 20 | (values 21 | '((content-type . (text/html))) 22 | " 23 | 24 | a 25 | ")) 26 | ; this works 27 | ; ("/style.css" (values '((content-type . (text/css)) (cache-control . ((max-age . 300)))) "")) 28 | ("/style.css" (values '((content-type . (text/css))) "body { color: red; }")) 29 | (else (values '() "not found"))))) 30 | (navigate-to "http://localhost:8080/a.html") 31 | (click (element-by-link-text "b")) 32 | (click (element-by-link-text "a")) 33 | (click (element-by-link-text "b")) 34 | (click (element-by-link-text "a")) 35 | (assert (equal? "http://localhost:8080/a.html" (current-url)))) 36 | 37 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - extract api reference from readme, add a quick start to the readme 2 | - add (selected? text) helper 3 | - appium, so i can test touch events finally 4 | - Complete all commands from specification 5 | - with-web-driver should also accept arguments, or maybe just one, the driver? 6 | - test-web-handler method, that installs the web server and navigates to it 7 | - check if these bug are also present in the current chromedriver? 8 | - report the bug to chromedriver: 9 | 10 | POST /session/6b2acd62d23a0e24dd925bd796305385/actions HTTP/1.1 11 | Content-Length: 174 12 | Host: localhost:53999 13 | Connection: close 14 | 15 | {"actions" : [{"id" : "keyboard0","type" : "key","actions" : []}, {"id" : "mouse0","type" : "pointer","actions" : [{"button" : 0,"type" : "pointerDown","x" : 10,"y" : 20}]}]} 16 | 17 | crashes chromedriver 18 | 19 | - report the bug to chromedriver 20 | 21 | POST /session/0b91e2565a5276a5740920bf1cf5fdbe/actions HTTP/1.1 22 | Content-Length: 327 23 | Host: localhost:33355 24 | Connection: close 25 | 26 | {"actions" : [{"id" : "keyboard0","type" : "key","actions" : [{"type" : "keyDown","value" : "a"}, {"type" : "pause","duration" : 100}, {"type" : "keyUp","value" : "a"}]}, {"id" : "mouse0","type" : "pointer","actions" : [{"type" : "pause","duration" : 0}, {"type" : "pause","duration" : 0}, {"type" : "pause","duration" : 0}]}]} 27 | 28 | The pause between the keyDown and the keyUp event takes double the time, 200 milliseconds. 29 | 30 | - potential bug: can *attribute* or *property* return an element, or an object? 31 | - wish: get-line should accept no argument to read from current input port 32 | - wish: map should accept vector in place of a list 33 | -------------------------------------------------------------------------------- /web/driver/key.scm: -------------------------------------------------------------------------------- 1 | (define-module (web driver key)) 2 | 3 | (use-modules 4 | (ice-9 match) 5 | (srfi srfi-1)) 6 | 7 | (define-public (key->unicode-char code) 8 | (if (equal? 1 (string-length code)) 9 | code 10 | (first 11 | (find 12 | (match-lambda ((char key-code) (string-ci=? key-code code))) 13 | char-key-codes)))) 14 | 15 | ; Mappings between single unicode character and keyevent codes 16 | ; Copy Pasted from the specification page 17 | ; ttps://w3c.github.io/webdriver/ 18 | 19 | (define char-key-codes 20 | ; normalized key value table 21 | '(("\uE001" "Cancel") 22 | ("\uE002" "Help") 23 | ("\uE003" "Backspace") 24 | ("\uE004" "Tab") 25 | ("\uE005" "Clear") 26 | ("\uE006" "Return") 27 | ("\uE007" "Enter") 28 | ("\uE008" "Shift") 29 | ("\uE009" "Control") 30 | ("\uE00A" "Alt") 31 | ("\uE00B" "Pause") 32 | ("\uE00C" "Escape") 33 | ("\uE00D" " ") 34 | ("\uE00E" "PageUp") 35 | ("\uE00F" "PageDown") 36 | ("\uE010" "End") 37 | ("\uE011" "Home") 38 | ("\uE012" "ArrowLeft") 39 | ("\uE013" "ArrowUp") 40 | ("\uE014" "ArrowRight") 41 | ("\uE015" "ArrowDown") 42 | ("\uE016" "Insert") 43 | ("\uE017" "Delete") 44 | ("\uE018" ";") 45 | ("\uE019" "=") 46 | ("\uE01A" "0") 47 | ("\uE01B" "1") 48 | ("\uE01C" "2") 49 | ("\uE01D" "3") 50 | ("\uE01E" "4") 51 | ("\uE01F" "5") 52 | ("\uE020" "6") 53 | ("\uE021" "7") 54 | ("\uE022" "8") 55 | ("\uE023" "9") 56 | ("\uE024" "*") 57 | ("\uE025" "+") 58 | ("\uE026" ",") 59 | ("\uE027" "-") 60 | ("\uE028" ".") 61 | ("\uE029" "/") 62 | ("\uE031" "F1") 63 | ("\uE032" "F2") 64 | ("\uE033" "F3") 65 | ("\uE034" "F4") 66 | ("\uE035" "F5") 67 | ("\uE036" "F6") 68 | ("\uE037" "F7") 69 | ("\uE038" "F8") 70 | ("\uE039" "F9") 71 | ("\uE03A" "F10") 72 | ("\uE03B" "F11") 73 | ("\uE03C" "F12") 74 | ("\uE03D" "Meta") 75 | ("\uE040" "ZenkakuHankaku") 76 | ("\uE050" "Shift") 77 | ("\uE051" "Control") 78 | ("\uE052" "Alt") 79 | ("\uE053" "Meta") 80 | ("\uE054" "PageUp") 81 | ("\uE055" "PageDown") 82 | ("\uE056" "End") 83 | ("\uE057" "Home") 84 | ("\uE058" "ArrowLeft") 85 | ("\uE059" "ArrowUp") 86 | ("\uE05A" "ArrowRight") 87 | ("\uE05B" "ArrowDown") 88 | ("\uE05C" "Insert") 89 | ("\uE05D" "Delete") 90 | 91 | ; Shifted character table 92 | ("`" "Backquote") 93 | ("\\" "Backslash") 94 | ("\uE003" "Backspace") 95 | ("[" "BracketLeft") 96 | ("]" "BracketRight") 97 | ("," "Comma") 98 | ("0" "Digit0") 99 | ("1" "Digit1") 100 | ("2" "Digit2") 101 | ("3" "Digit3") 102 | ("4" "Digit4") 103 | ("5" "Digit5") 104 | ("6" "Digit6") 105 | ("7" "Digit7") 106 | ("8" "Digit8") 107 | ("9" "Digit9") 108 | ("=" "Equal") 109 | ("<" "IntlBackslash") 110 | ("a" "KeyA") 111 | ("b" "KeyB") 112 | ("c" "KeyC") 113 | ("d" "KeyD") 114 | ("e" "KeyE") 115 | ("f" "KeyF") 116 | ("g" "KeyG") 117 | ("h" "KeyH") 118 | ("i" "KeyI") 119 | ("j" "KeyJ") 120 | ("k" "KeyK") 121 | ("l" "KeyL") 122 | ("m" "KeyM") 123 | ("n" "KeyN") 124 | ("o" "KeyO") 125 | ("p" "KeyP") 126 | ("q" "KeyQ") 127 | ("r" "KeyR") 128 | ("s" "KeyS") 129 | ("t" "KeyT") 130 | ("u" "KeyU") 131 | ("v" "KeyV") 132 | ("w" "KeyW") 133 | ("x" "KeyX") 134 | ("y" "KeyY") 135 | ("z" "KeyZ") 136 | ("-" "Minus") 137 | ("." "Period") 138 | ("'" "Quote") 139 | (";" "Semicolon") 140 | ("/" "Slash") 141 | ("\uE00A" "AltLeft") 142 | ("\uE052" "AltRight") 143 | ("\uE009" "ControlLeft") 144 | ("\uE051" "ControlRight") 145 | ("\uE006" "Enter") 146 | ("\uE03D" "OSLeft") 147 | ("\uE053" "OSRight") 148 | ("\uE008" "ShiftLeft") 149 | ("\uE050" "ShiftRight") 150 | (" " "Space") 151 | ("\uE004" "Tab") 152 | ("\uE017" "Delete") 153 | ("\uE010" "End") 154 | ("\uE002" "Help") 155 | ("\uE011" "Home") 156 | ("\uE016" "Insert") 157 | ("\uE00F" "PageDown") 158 | ("\uE00E" "PageUp") 159 | ("\uE015" "ArrowDown") 160 | ("\uE012" "ArrowLeft") 161 | ("\uE014" "ArrowRight") 162 | ("\uE013" "ArrowUp") 163 | ("\uE00C" "Escape") 164 | ("\uE031" "F1") 165 | ("\uE032" "F2") 166 | ("\uE033" "F3") 167 | ("\uE034" "F4") 168 | ("\uE035" "F5") 169 | ("\uE036" "F6") 170 | ("\uE037" "F7") 171 | ("\uE038" "F8") 172 | ("\uE039" "F9") 173 | ("\uE03A" "F10") 174 | ("\uE03B" "F11") 175 | ("\uE03C" "F12") 176 | ("\uE01A" "Numpad0") 177 | ("\uE01B" "Numpad1") 178 | ("\uE01C" "Numpad2") 179 | ("\uE01D" "Numpad3") 180 | ("\uE01E" "Numpad4") 181 | ("\uE01F" "Numpad5") 182 | ("\uE020" "Numpad6") 183 | ("\uE021" "Numpad7") 184 | ("\uE022" "Numpad8") 185 | ("\uE023" "Numpad9") 186 | ("\uE025" "NumpadAdd") 187 | ("\uE026" "NumpadComma") 188 | ("\uE028" "NumpadDecimal") 189 | ("\uE029" "NumpadDivide") 190 | ("\uE007" "NumpadEnter") 191 | ("\uE024" "NumpadMultiply") 192 | ("\uE027" "NumpadSubtract"))) 193 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## guile-web-driver 2 | 3 | This is a web-driver, or selenium 2, client. 4 | It's purpose is to automate browsers, specifically for automatic web server testing. 5 | Chrome or Firefox can be used as the automated browsers, 6 | or it can connect to arbitrary server providing webdriver interface. 7 | The client implements most of the webdriver [specification](https://www.w3.org/TR/webdriver2/). 8 | 9 | ### Requirements 10 | 11 | - guile version 2.2 12 | - guile-json library from http://download.savannah.gnu.org/releases/guile-json/guile-json-4.7.3.tar.gz 13 | - Optional chromedriver command and either chrome or chromium browser. 14 | Some distribution (arch) install chromedriver as part of chromium package, 15 | some others (debian) provide a separate package (chromium-driver). 16 | Required for unit tests. 17 | - Optional [geckodriver](https://github.com/mozilla/geckodriver/) and mozilla firefox browser. 18 | Required for unit tests. 19 | - Optionally for unit testing, hdt library is required from https://github.com/her01n/hdt 20 | 21 | ### Licence 22 | 23 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 24 | 25 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 26 | 27 | You should have received a copy of the GNU General Public License along with this program. If not, see