;; -*- geiser-scheme: chicken -*- (import (r7rs)) (import (scheme base)) (import (srfi 1) (srfi 180)) (import coops coops-extras coops-primitive-objects coops-utils) (import (chicken process) (chicken gc) (chicken condition)) (import uri-common) (import intarweb) (import http-client) (define-class () ((base-url accessor: base-url) (stdout accessor: stdout) (stdin accessor: stdin) (stderr accessor: stderr) (pid accessor: pid) (session accessor: session))) (define-method (initialize-instance (browser )) (set-finalizer! browser (lambda (obj) (terminate browser))) (call-next-method)) (define (raise-error error-alist) (print "BOOM!") (error (alist-ref 'error error-alist) error-alist)) (define-method (read-reply (browser )) (lambda () (json-read))) (define-method (send (browser ) struct path method) (let ((reply (with-input-from-request (make-request method: method uri: (uri-reference (string-append (base-url browser) path)) headers: (headers `((content-type application/json)))) (lambda () (json-write struct)) (read-reply browser)))) (if (and (list? reply) (find (lambda (x) (eqv? (car x) 'error)) reply)) (raise-error (alist-ref 'error reply)) reply))) (define-method (initialize-session (browser )) (set! (session browser) (alist-ref 'sessionId (send browser '() "session" 'POST)))) (define-method (terminate-session (browser )) (send browser '() (string-append "session/" (session browser)) 'DELETE)) (define-method (send-with-session (browser ) struct path method) (send browser struct (string-append "session/" (session browser) "/" path) method)) (define-method (terminate (browser )) (call/cc (lambda (c) (with-exception-handler (lambda (x) (c #t)) (lambda () (terminate-session browser) (process-signal (pid browser)) (close-input-port (stdout browser)) (close-input-port (stderr browser)) (close-output-port (stdin browser))))))) (define-class ()) (define (firefox #!key ip ip port port) (let-values (((stdout stdin pid stderr) (process* "geckodriver"))) (make 'base-url (string-append "http://" (or ip "127.0.0.1") ":" (or port "4444") "/") 'stdout stdout 'stdin stdin 'stderr stderr 'pid pid))) (define-method (read-reply (browser )) (lambda () (alist-ref 'value (json-read)))) ;; ======================= ;; Main API Implementation ;; ======================= (define-method (status (browser )) (send browser '() "status" 'GET)) (define-method (maximize! (browser )) (send-with-session browser '() "maximize" 'POST)) (define-method (minimize! (browser )) (send-with-session browser '() "minimize" 'POST)) (define-method (fullscreen! (browser )) (send-with-session browser '() "fullscreen" 'POST)) (define-method (forward! (browser )) (send-with-session browser '() "forward" 'POST)) (define-method (back! (browser )) (send-with-session browser '() "back" 'POST)) (define-method (refresh! (browser )) (send-with-session browser '() "refresh" 'POST)) (define-method (url (browser )) (send-with-session browser '() "url" 'GET)) (define-method (url! (browser ) (uri )) (send-with-session browser `((url . ,uri)) "url" 'POST)) (define-method (title (browser )) (send-with-session browser '() "title" 'GET)) (define-method (source (browser )) (send-with-session browser '() "source" 'GET)) ;; ============================= ;; Timeout Settings ;; ============================= (define-record timeouts script page-load implicit) (define-method (get-timeouts (browser )) (let ((result (send-with-session browser '() "timeouts" 'GET))) (make-timeouts (alist-ref 'script result) (alist-ref 'pageLoad result) (alist-ref 'implicit result)))) (define-method (timeouts! (browser ) timeouts) (let ((timeouts-alist `((script . ,(timeouts-script timeouts)) (pageLoad . ,(timeouts-page-load timeouts)) (implicit . ,(timeouts-implicit timeouts))))) (send-with-session browser timeouts-alist "timeouts" 'POST))) ;; ============================= ;; Elements ;; ============================= (define-record element id) (define locator-strategies '((css: . "css selector") (linktext: . "link text") (partial-linktext: . "partial link text") (tag: . "tag name") (xpath: . "xpath"))) (define-method (get-element (browser ) (locator-strategy ) (locator )) (make-element (cdar (send-with-session browser `((using . ,(alist-ref locator-strategy locator-strategies)) (value . ,locator)) "element" 'POST)))) (define-method (get-elements (browser ) (locator-strategy ) (locator )) (map (lambda (elem) (make-element (cdar elem))) (send-with-session browser `((using . ,(alist-ref locator-strategy locator-strategies)) (value . ,locator)) "elements" 'POST))) (define-method (element-selected? (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/selected") 'GET)) (define-method (element-attribute (browser ) elem attribute) (send-with-session browser '() (string-append "element/" (element-id elem) "/attribute/" attribute) 'GET)) (define-method (element-property (browser ) elem property) (send-with-session browser '() (string-append "element/" (element-id elem) "/property/" property) 'GET)) (define-method (element-css-value (browser ) elem property) (send-with-session browser '() (string-append "element/" (element-id elem) "/css/" property) 'GET)) (define-method (element-text (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/text") 'GET)) (define-method (element-tag-name (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/name") 'GET)) (define-method (element-rect (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/rect") 'GET)) (define-method (element-enabled? (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/enabled") 'GET)) (define-method (element-computed-role (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/computedrole") 'GET)) (define-method (element-computed-label (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/computedlabel") 'GET)) (define-method (element-click (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/click") 'POST)) (define-method (element-clear (browser ) elem) (send-with-session browser '() (string-append "element/" (element-id elem) "/clear") 'POST)) (define-method (element-type-text (browser ) elem text) (send-with-session browser `((text . ,text)) (string-append "element/" (element-id elem) "/value") 'POST))