├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── api_service.go ├── capabilities.go ├── doc.go ├── errors.go ├── errors_test.go ├── examples ├── error-handling │ └── main.go ├── getting-started │ └── main.go └── hackernews │ └── main.go ├── remote_driver.go ├── remote_driver_alert.go ├── remote_driver_alert_test.go ├── remote_driver_command.go ├── remote_driver_command_test.go ├── remote_driver_cookie.go ├── remote_driver_cookie_test.go ├── remote_driver_document.go ├── remote_driver_document_test.go ├── remote_driver_element.go ├── remote_driver_element_test.go ├── remote_driver_helpers.go ├── remote_driver_navigation.go ├── remote_driver_navigation_test.go ├── remote_driver_screenshot.go ├── remote_driver_screenshot_test.go ├── remote_driver_session.go ├── remote_driver_session_test.go ├── remote_driver_test.go ├── remote_element.go ├── remote_element_test.go ├── test └── integration_tests │ ├── alert_acceptalert_test.go │ ├── alert_alerttext_test.go │ ├── alert_dismissalert_test.go │ ├── alert_sendalerttext_test.go │ ├── command_closewindow_test.go │ ├── command_maximizewindow_test.go │ ├── command_setwindowsize_test.go │ ├── command_switchtoframe_test.go │ ├── command_switchtoparentframe_test.go │ ├── command_windowhandle_test.go │ ├── command_windowhandles_test.go │ ├── command_windowsize_test.go │ ├── cookie_addcookie_test.go │ ├── cookie_allcookies_test.go │ ├── cookie_cookie_test.go │ ├── cookie_deletecookie_test.go │ ├── document_executescript_test.go │ ├── document_pagesource_test.go │ ├── element_attribute_test.go │ ├── element_clear_test.go │ ├── element_click_test.go │ ├── element_cssclass_test.go │ ├── element_enabled_test.go │ ├── element_findelement_test.go │ ├── element_findelements_test.go │ ├── element_rectangle_test.go │ ├── element_selected_test.go │ ├── element_sendkeys_test.go │ ├── element_tagname_test.go │ ├── element_text_test.go │ ├── helpers_elementpresent_test.go │ ├── helpers_untilurlis_test.go │ ├── navigate_back_test.go │ ├── navigate_forward_test.go │ ├── navigate_go_test.go │ ├── navigate_refresh_test.go │ ├── navigate_title_test.go │ ├── screenshot_screenshot_test.go │ ├── session_create_test.go │ ├── session_delete_test.go │ ├── session_general_test.go │ ├── session_settimeout_test.go │ ├── session_status_test.go │ └── test.go └── web_driver.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | *.swp 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.x 4 | script: go test ./*.go -v 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Bunsen 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-selenium 2 | 3 | [![Build Status](https://travis-ci.org/bunsenapp/go-selenium.svg?branch=master)](https://travis-ci.org/bunsenapp/go-selenium) 4 | [![GoDoc](https://godoc.org/github.com/bunsenapp/go-selenium?status.svg)](https://godoc.org/github.com/bunsenapp/go-selenium) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/bunsenapp/go-selenium)](https://goreportcard.com/report/github.com/bunsenapp/go-selenium) 6 | 7 | ## Maintainer Required 8 | 9 | This package is no longer maintained. The author no longer writes automated tests with Selenium, and will only fix this package should there be a major security issue. 10 | 11 | If you or someone you know is interested in maintaining this package, please raise an issue and tag me (@elsyms) in it. 12 | 13 | ## Introduction 14 | 15 | Yes, yet another Selenium Web Driver library has been brought to the table. This one, however, is slightly different. 16 | 17 | * Easy to understand. 18 | * Full test coverage by unit tests and integration tests. 19 | * Excellent support; we use this in our main project so if you find an issue - it'll likely impact us! 20 | * Idiomatic, structured code with no gimmicks. 21 | * Simple errors that describe what has happened and why. 22 | 23 | ## Installation 24 | 25 | As with all Go libraries, go-selenium is easy to install. Simply run the below command: 26 | 27 | `go get github.com/bunsenapp/go-selenium` 28 | 29 | and then import the library in your project: 30 | 31 | `import "github.com/bunsenapp/go-selenium"` 32 | 33 | ## Getting started 34 | 35 | Prior to using this library you need to ensure that you have a Selenium instance running (standalone or grid is fine). If you don't know how to do this, there is a small section called 'Getting Selenium running' below. 36 | 37 | Please see the [examples/getting-started/main.go](https://github.com/bunsenapp/go-selenium/blob/master/examples/getting-started/main.go) file. 38 | 39 | ## Examples 40 | 41 | Further examples, including tests of HackerNews (c), are available within the `examples` directory. 42 | 43 | ## Documentation 44 | 45 | All documentation is available on the godoc.org website: [https://godoc.org/github.com/bunsenapp/go-selenium](https://godoc.org/github.com/bunsenapp/go-selenium). 46 | 47 | ## Getting Selenium running 48 | 49 | ### With Docker 50 | 51 | 1. Choose an image from the following URL: https://github.com/SeleniumHQ/docker-selenium 52 | 2. Execute the following Docker command replacing the image with your chosen one: `docker run -d -p 4444:4444 --name selenium selenium/standalone-firefox`. 53 | 54 | ### Without Docker 55 | 56 | 1. Download the Selenium standalone server from the following URL: http://www.seleniumhq.org/download/ 57 | 2. Download the appropriate web driver executable and include it in your path. For Firefox, that will be the Gecko driver. 58 | 3. Run the Selenium server with the following command: `java -jar selenium-server-standalone-3.0.1.jar`. 59 | -------------------------------------------------------------------------------- /api_service.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | 10 | "errors" 11 | ) 12 | 13 | type apiServicer interface { 14 | performRequest(string, string, io.Reader) ([]byte, error) 15 | } 16 | 17 | type requestError struct { 18 | State string `json:"state"` 19 | Value requestErrorValue `json:"value"` 20 | } 21 | 22 | func (r requestError) Error() string { 23 | return fmt.Sprintf("Invalid status code returned, message: %v, information: %v", r.State, r.Value.Message) 24 | } 25 | 26 | type requestErrorValue struct { 27 | Message string `json:"localizedMessage"` 28 | } 29 | 30 | type seleniumAPIService struct{} 31 | 32 | func (a seleniumAPIService) performRequest(url string, method string, body io.Reader) ([]byte, error) { 33 | request, err := http.NewRequest(method, url, body) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | client := http.Client{} 39 | resp, err := client.Do(request) 40 | if err != nil { 41 | return nil, fmt.Errorf("%s: an unexpected communication failure occurred, error: %s", method, err.Error()) 42 | } 43 | 44 | defer resp.Body.Close() 45 | 46 | var buf bytes.Buffer 47 | buf.ReadFrom(resp.Body) 48 | r := buf.Bytes() 49 | 50 | if resp.StatusCode != 200 { 51 | var reqErr requestError 52 | var errStr string 53 | 54 | err := json.Unmarshal(r, &reqErr) 55 | if err == nil { 56 | return nil, &reqErr 57 | } 58 | 59 | errStr = fmt.Sprintf("Status code %v returned with no body", resp.StatusCode) 60 | return nil, errors.New(errStr) 61 | } 62 | 63 | return r, nil 64 | } 65 | -------------------------------------------------------------------------------- /capabilities.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import "encoding/json" 4 | 5 | // Browser defines a supported selenium enabled browser. 6 | type Browser interface { 7 | BrowserName() string 8 | } 9 | 10 | // Browser represents a browser to run within Selenium. 11 | type browser struct { 12 | browserName string 13 | } 14 | 15 | // BrowserName returns the browser name assigned to the current browser object. 16 | func (b browser) BrowserName() string { 17 | return b.browserName 18 | } 19 | 20 | // FirefoxBrowser returns a Firefox browser object. 21 | func FirefoxBrowser() Browser { 22 | return browser{"firefox"} 23 | } 24 | 25 | // ChromeBrowser returns a Chrome browser object. 26 | func ChromeBrowser() Browser { 27 | return browser{"chrome"} 28 | } 29 | 30 | // AndroidBrowser returns an Android browser object. 31 | func AndroidBrowser() Browser { 32 | return browser{"android"} 33 | } 34 | 35 | // HTMLUnitBrowser returns a HTMLUnit browser object. 36 | func HTMLUnitBrowser() Browser { 37 | return browser{"htmlunit"} 38 | } 39 | 40 | // InternetExplorerBrowser returns an IE browser object. 41 | func InternetExplorerBrowser() Browser { 42 | return browser{"internetexplorer"} 43 | } 44 | 45 | // IPhoneBrowser returns an IPhone browser object. 46 | func IPhoneBrowser() Browser { 47 | return browser{"iphone"} 48 | } 49 | 50 | // IPadBrowser returns an IPad browser object. 51 | func IPadBrowser() Browser { 52 | return browser{"ipad"} 53 | } 54 | 55 | // OperaBrowser returns an Opera browser object. 56 | func OperaBrowser() Browser { 57 | return browser{"opera"} 58 | } 59 | 60 | // SafariBrowser returns a Safari browser object. 61 | func SafariBrowser() Browser { 62 | return browser{"safari"} 63 | } 64 | 65 | // Capabilities represents the capabilities defined in the W3C specification. 66 | // The main capability is the browser, which can be set by calling one of the 67 | // \wBrowser\(\) methods. 68 | type Capabilities struct { 69 | browser Browser 70 | } 71 | 72 | // Browser yields the browser capability assigned to the current Capabilities 73 | // object.. 74 | func (c *Capabilities) Browser() Browser { 75 | if c.browser != nil { 76 | return c.browser 77 | } 78 | 79 | return browser{} 80 | } 81 | 82 | // SetBrowser sets the browser capability to be one of the allowed browsers. 83 | func (c *Capabilities) SetBrowser(b Browser) { 84 | c.browser = b 85 | } 86 | 87 | func (c *Capabilities) toJSON() (string, error) { 88 | capabilities := map[string]map[string]interface{}{ 89 | "desiredCapabilities": { 90 | "browserName": c.browser.BrowserName(), 91 | }, 92 | } 93 | 94 | capabilitiesJSON, err := json.Marshal(capabilities) 95 | if err != nil { 96 | return "", err 97 | } 98 | 99 | return string(capabilitiesJSON), nil 100 | } 101 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package goselenium is a Selenium web driver library written in Go. 2 | package goselenium 3 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import "fmt" 4 | 5 | // ErrorResponse is what is returned from the Selenium API when an error 6 | // occurs. 7 | type ErrorResponse struct { 8 | Message string 9 | State string 10 | } 11 | 12 | // CommunicationError is the result of a communication failure between 13 | // this library and the WebDriver API. 14 | type CommunicationError struct { 15 | url string 16 | Response *ErrorResponse 17 | method string 18 | } 19 | 20 | // Error returns a formatted communication error string. 21 | func (c CommunicationError) Error() string { 22 | return fmt.Sprintf("%s: api error, url: %s, err: %+v", c.method, c.url, c.Response) 23 | } 24 | 25 | // IsCommunicationError checks whether an error is a selenium communication 26 | // error. 27 | func IsCommunicationError(err error) bool { 28 | _, ok := err.(CommunicationError) 29 | return ok 30 | } 31 | 32 | func newCommunicationError(err error, method string, url string, resp []byte) CommunicationError { 33 | var convertedResponse ErrorResponse 34 | 35 | reqErr, ok := err.(*requestError) 36 | if ok { 37 | convertedResponse = ErrorResponse{ 38 | Message: reqErr.Value.Message, 39 | State: reqErr.State, 40 | } 41 | } 42 | 43 | return CommunicationError{ 44 | url: url, 45 | Response: &convertedResponse, 46 | method: method, 47 | } 48 | } 49 | 50 | // UnmarshallingError is the result of an unmarshalling failure of a JSON 51 | // string. 52 | type UnmarshallingError struct { 53 | err error 54 | json string 55 | method string 56 | } 57 | 58 | // Error returns a formatted unmarshalling error string. 59 | func (u UnmarshallingError) Error() string { 60 | return fmt.Sprintf("%s: unmarshalling error, json: %s, err: %s", u.method, u.json, u.err) 61 | } 62 | 63 | // IsUnmarshallingError checks whether an error is a selenium unmarshalling 64 | // error. 65 | func IsUnmarshallingError(err error) bool { 66 | _, ok := err.(UnmarshallingError) 67 | return ok 68 | } 69 | 70 | func newUnmarshallingError(err error, method string, json string) UnmarshallingError { 71 | return UnmarshallingError{ 72 | err: err, 73 | json: json, 74 | method: method, 75 | } 76 | } 77 | 78 | // MarshallingError is an error that is returned when a json.Marshal error occurs. 79 | type MarshallingError struct { 80 | err error 81 | object interface{} 82 | method string 83 | } 84 | 85 | // Error returns a formatted marshalling error string. 86 | func (m MarshallingError) Error() string { 87 | return fmt.Sprintf("%s: marshalling error for object %+v, err: %s", m.method, m.object, m.err.Error()) 88 | } 89 | 90 | // IsMarshallingError checks whether an error is a marshalling error. 91 | func IsMarshallingError(err error) bool { 92 | _, ok := err.(MarshallingError) 93 | return ok 94 | } 95 | 96 | func newMarshallingError(err error, method string, obj interface{}) MarshallingError { 97 | return MarshallingError{ 98 | err: err, 99 | object: obj, 100 | method: method, 101 | } 102 | } 103 | 104 | // SessionIDError is an error that is returned when the session id is 105 | // invalid. This value will contain the method that the session error occurred 106 | // in. 107 | type SessionIDError string 108 | 109 | // Error returns a formatted session error string. 110 | func (s SessionIDError) Error() string { 111 | return fmt.Sprintf("%s: session id is invalid (have you created a session yet?)", string(s)) 112 | } 113 | 114 | // IsSessionIDError checks whether an error is due to a session ID not being 115 | // set. 116 | func IsSessionIDError(err error) bool { 117 | _, ok := err.(SessionIDError) 118 | return ok 119 | } 120 | 121 | func newSessionIDError(method string) SessionIDError { 122 | return SessionIDError(method) 123 | } 124 | 125 | // InvalidURLError is an error that is returned whenever a URL is not correctly 126 | // formatted. 127 | type InvalidURLError string 128 | 129 | // Error returns the formatted invalid error string. 130 | func (i InvalidURLError) Error() string { 131 | return fmt.Sprintf("invalid url: %s", string(i)) 132 | } 133 | 134 | // IsInvalidURLError checks whether an error is due to the URL being incorrectly 135 | // formatted. 136 | func IsInvalidURLError(err error) bool { 137 | _, ok := err.(InvalidURLError) 138 | return ok 139 | } 140 | 141 | // InvalidURLError 142 | func newInvalidURLError(url string) InvalidURLError { 143 | return InvalidURLError(url) 144 | } 145 | -------------------------------------------------------------------------------- /errors_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func communicationError() error { 9 | return newCommunicationError(errors.New(":<"), "Test", "", nil) 10 | } 11 | 12 | func sessionError() error { 13 | return newSessionIDError("Test") 14 | } 15 | 16 | func unmarshallingError() error { 17 | return newUnmarshallingError(errors.New(":<"), "Test", "") 18 | } 19 | 20 | func marshallingError() error { 21 | return newMarshallingError(errors.New(":<"), "Test", "test") 22 | } 23 | 24 | func Test_Errors_CommunicationErrorCanBeCastSuccessfully(t *testing.T) { 25 | e := communicationError() 26 | 27 | back, ok := e.(CommunicationError) 28 | if !ok || back.method != "Test" { 29 | t.Errorf("Could not assert error") 30 | } 31 | } 32 | 33 | func Test_Errors_SessionErrorCanBeCastSuccessfully(t *testing.T) { 34 | e := sessionError() 35 | 36 | _, ok := e.(SessionIDError) 37 | if !ok { 38 | t.Errorf("Could not assert error") 39 | } 40 | } 41 | 42 | func Test_Errors_UnmarshallingErrorCanBeCastSuccessfully(t *testing.T) { 43 | e := unmarshallingError() 44 | 45 | body, ok := e.(UnmarshallingError) 46 | if !ok || body.method != "Test" { 47 | t.Errorf("Could not assert error") 48 | } 49 | } 50 | 51 | func Test_Errors_MarshallingErrorCanBeCastSuccessfully(t *testing.T) { 52 | e := marshallingError() 53 | 54 | body, ok := e.(MarshallingError) 55 | if !ok || body.method != "Test" { 56 | t.Errorf("Could not assert error") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/error-handling/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | goselenium "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func main() { 10 | // Create the capabilities. 11 | capabilities := goselenium.Capabilities{} 12 | capabilities.SetBrowser(goselenium.FirefoxBrowser()) 13 | 14 | // Create the driver. 15 | driver, err := goselenium.NewSeleniumWebDriver("http://localhost:4444/wd/hub/", capabilities) 16 | if err != nil { 17 | fmt.Println("Error creating web driver.") 18 | return 19 | } 20 | 21 | // Create the session. 22 | _, err = driver.CreateSession() 23 | if err != nil { 24 | fmt.Println("Error creating session.") 25 | return 26 | } 27 | 28 | // Navigate to Google. 29 | _, err = driver.Go("https://www.google.com") 30 | if err != nil { 31 | fmt.Println("An error occurred whilst visiting URL.") 32 | return 33 | } 34 | 35 | // Find a non existent element for it to error. 36 | _, err = driver.FindElement(goselenium.ByCSSSelector("mynonexistentelement")) 37 | if err != nil { 38 | // Switch the different types of errors. You do not need to do this in 39 | // every call and can simply abstract it behind a function. If you 40 | // don't want to handle the custom errors, they all implement the 41 | // Error interface meaning it'll work anywhere your normal errors do. 42 | switch err.(type) { 43 | case goselenium.CommunicationError: 44 | e := err.(goselenium.CommunicationError) 45 | // Switch the different states that we want to handle. 46 | switch e.Response.State { 47 | case goselenium.UnknownError: 48 | fmt.Println("An unknown error occurred.") 49 | case goselenium.SessionNotCreated: 50 | fmt.Println("The session was not created.") 51 | case goselenium.NoSuchElement: 52 | fmt.Println("Failed to find element. Example passed!") 53 | } 54 | case goselenium.UnmarshallingError: 55 | fmt.Println("An unmarshalling error occurred :<") 56 | } 57 | } 58 | 59 | // Delete the session. 60 | driver.DeleteSession() 61 | } 62 | -------------------------------------------------------------------------------- /examples/getting-started/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func main() { 10 | // Create a capabilities object. 11 | capabilities := goselenium.Capabilities{} 12 | 13 | // Populate it with the browser you wish to use. 14 | capabilities.SetBrowser(goselenium.FirefoxBrowser()) 15 | 16 | // Initialise a new web driver. 17 | driver, err := goselenium.NewSeleniumWebDriver("http://localhost:4444/wd/hub", capabilities) 18 | if err != nil { 19 | fmt.Println(err) 20 | return 21 | } 22 | 23 | // Create a session. 24 | _, err = driver.CreateSession() 25 | if err != nil { 26 | fmt.Println(err) 27 | return 28 | } 29 | 30 | // Defer the deletion of the session. 31 | defer driver.DeleteSession() 32 | 33 | // Navigate to Google. 34 | _, err = driver.Go("https://www.google.com") 35 | if err != nil { 36 | fmt.Println(err) 37 | } 38 | 39 | // Hooray, we navigated to Google! 40 | fmt.Println("Successfully navigated to Google!") 41 | } 42 | -------------------------------------------------------------------------------- /examples/hackernews/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | goselenium "github.com/bunsenapp/go-selenium" 8 | ) 9 | 10 | func main() { 11 | // Create capabilities, driver etc. 12 | capabilities := goselenium.Capabilities{} 13 | capabilities.SetBrowser(goselenium.FirefoxBrowser()) 14 | 15 | driver, err := goselenium.NewSeleniumWebDriver("http://localhost:4444/wd/hub", capabilities) 16 | if err != nil { 17 | fmt.Println(err) 18 | return 19 | } 20 | 21 | _, err = driver.CreateSession() 22 | if err != nil { 23 | fmt.Println(err) 24 | return 25 | } 26 | 27 | // Delete the session once this function is completed. 28 | defer driver.DeleteSession() 29 | 30 | // Navigate to the HackerNews website. 31 | _, err = driver.Go("https://news.ycombinator.com") 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | // Click the 'new' link at the top 38 | el, err := driver.FindElement(goselenium.ByCSSSelector("a[href='newest']")) 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | 44 | // Click the link. 45 | _, err = el.Click() 46 | if err != nil { 47 | fmt.Println(err) 48 | return 49 | } 50 | 51 | // Wait until the URL has changed with a timeout of 1 second and a check 52 | // interval of 10ms.. 53 | newLink := "https://news.ycombinator.com/newest" 54 | ok := driver.Wait(goselenium.UntilURLIs(newLink), 1*time.Second, 10*time.Millisecond) 55 | if !ok { 56 | fmt.Println("Wait timed out :<") 57 | return 58 | } 59 | 60 | // Woohoo! We have successfully navigated to a page. 61 | fmt.Println("Successfully navigated to URL " + newLink) 62 | } 63 | -------------------------------------------------------------------------------- /remote_driver.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io" 7 | "strings" 8 | 9 | "errors" 10 | ) 11 | 12 | // NewSeleniumWebDriver creates a new instance of a Selenium web driver with a 13 | // service URL (usually http://domain:port/wd/hub) and a Capabilities object. 14 | // This method will return validation errors if the Selenium URL is invalid or 15 | // the required capabilities (BrowserName) are not set. 16 | func NewSeleniumWebDriver(serviceURL string, capabilities Capabilities) (WebDriver, error) { 17 | if serviceURL == "" { 18 | return nil, errors.New("Provided Selenium URL is invalid") 19 | } 20 | 21 | urlValid := strings.HasPrefix(serviceURL, "http://") || strings.HasPrefix(serviceURL, "https://") 22 | if !urlValid { 23 | return nil, errors.New("Provided Selenium URL is invalid.") 24 | } 25 | 26 | browser := capabilities.Browser() 27 | hasBrowserCapability := browser.BrowserName() != "" 28 | if !hasBrowserCapability { 29 | return nil, errors.New("An invalid capabilities object was provided.") 30 | } 31 | 32 | if strings.HasSuffix(serviceURL, "/") { 33 | serviceURL = strings.TrimSuffix(serviceURL, "/") 34 | } 35 | 36 | driver := &seleniumWebDriver{ 37 | seleniumURL: serviceURL, 38 | capabilities: &capabilities, 39 | apiService: &seleniumAPIService{}, 40 | } 41 | 42 | return driver, nil 43 | } 44 | 45 | // SessionScriptTimeout creates an appropriate Timeout implementation for the 46 | // script timeout. 47 | func SessionScriptTimeout(to int) Timeout { 48 | return timeout{ 49 | timeoutType: "script", 50 | timeout: to, 51 | } 52 | } 53 | 54 | // SessionPageLoadTimeout creates an appropriate Timeout implementation for the 55 | // page load timeout. 56 | func SessionPageLoadTimeout(to int) Timeout { 57 | return timeout{ 58 | timeoutType: "page load", 59 | timeout: to, 60 | } 61 | } 62 | 63 | // SessionImplicitWaitTimeout creates an appropriate timeout implementation for the 64 | // session implicit wait timeout. 65 | func SessionImplicitWaitTimeout(to int) Timeout { 66 | return timeout{ 67 | timeoutType: "implicit", 68 | timeout: to, 69 | } 70 | } 71 | 72 | // ByIndex accepts an integer that represents what the index of an element is 73 | // and returns the appropriate By implementation. 74 | func ByIndex(index uint) By { 75 | return by{ 76 | t: "index", 77 | value: index, 78 | } 79 | } 80 | 81 | // ByCSSSelector accepts a CSS selector (i.e. ul#id > a) for use in the 82 | // FindElement(s) functions. 83 | func ByCSSSelector(selector string) By { 84 | return by{ 85 | t: "css selector", 86 | value: selector, 87 | } 88 | } 89 | 90 | // ByLinkText is used to find an anchor element by its innerText. 91 | func ByLinkText(text string) By { 92 | return by{ 93 | t: "link text", 94 | value: text, 95 | } 96 | } 97 | 98 | // ByPartialLinkText works the same way as ByLinkText but performs a search 99 | // where the link text contains the string passed in instead of a full match. 100 | func ByPartialLinkText(text string) By { 101 | return by{ 102 | t: "partial link text", 103 | value: text, 104 | } 105 | } 106 | 107 | // ByXPath utilises the xpath to find elements (see http://www.guru99.com/xpath-selenium.html). 108 | func ByXPath(path string) By { 109 | return by{ 110 | t: "xpath", 111 | value: path, 112 | } 113 | } 114 | 115 | type seleniumWebDriver struct { 116 | seleniumURL string 117 | sessionID string 118 | capabilities *Capabilities 119 | apiService apiServicer 120 | } 121 | 122 | func (s *seleniumWebDriver) DriverURL() string { 123 | return s.seleniumURL 124 | } 125 | 126 | func (s *seleniumWebDriver) stateRequest(req *request) (*stateResponse, error) { 127 | var response stateResponse 128 | var err error 129 | 130 | resp, err := s.apiService.performRequest(req.url, req.method, req.body) 131 | if err != nil { 132 | return nil, newCommunicationError(err, req.callingMethod, req.url, resp) 133 | } 134 | 135 | err = json.Unmarshal(resp, &response) 136 | if err != nil { 137 | return nil, newUnmarshallingError(err, req.callingMethod, string(resp)) 138 | } 139 | 140 | return &response, nil 141 | } 142 | 143 | func (s *seleniumWebDriver) valueRequest(req *request) (*valueResponse, error) { 144 | var response valueResponse 145 | var err error 146 | 147 | resp, err := s.apiService.performRequest(req.url, req.method, req.body) 148 | if err != nil { 149 | return nil, newCommunicationError(err, req.callingMethod, req.url, resp) 150 | } 151 | 152 | err = json.Unmarshal(resp, &response) 153 | if err != nil { 154 | return nil, newUnmarshallingError(err, req.callingMethod, string(resp)) 155 | } 156 | 157 | return &response, nil 158 | } 159 | 160 | func (s *seleniumWebDriver) elementRequest(req *elRequest) ([]byte, error) { 161 | b := map[string]interface{}{ 162 | "using": req.by.Type(), 163 | "value": req.by.Value(), 164 | } 165 | bJSON, err := json.Marshal(b) 166 | if err != nil { 167 | return nil, newMarshallingError(err, req.callingMethod, bJSON) 168 | } 169 | 170 | body := bytes.NewReader(bJSON) 171 | resp, err := s.apiService.performRequest(req.url, req.method, body) 172 | if err != nil { 173 | return nil, newCommunicationError(err, req.callingMethod, req.url, resp) 174 | } 175 | 176 | return resp, nil 177 | } 178 | 179 | func (s *seleniumWebDriver) scriptRequest(script string, url string, method string) (*ExecuteScriptResponse, error) { 180 | r := map[string]interface{}{ 181 | "script": script, 182 | "args": []string{""}, 183 | } 184 | b, err := json.Marshal(r) 185 | if err != nil { 186 | return nil, newMarshallingError(err, method, r) 187 | } 188 | body := bytes.NewReader(b) 189 | resp, err := s.valueRequest(&request{ 190 | url: url, 191 | method: "POST", 192 | body: body, 193 | callingMethod: method, 194 | }) 195 | if err != nil { 196 | return nil, err 197 | } 198 | 199 | return &ExecuteScriptResponse{State: resp.State, Response: resp.Value}, nil 200 | } 201 | 202 | type timeout struct { 203 | timeoutType string 204 | timeout int 205 | } 206 | 207 | func (t timeout) Type() string { 208 | return t.timeoutType 209 | } 210 | 211 | func (t timeout) Timeout() int { 212 | return t.timeout 213 | } 214 | 215 | type request struct { 216 | url string 217 | method string 218 | body io.Reader 219 | callingMethod string 220 | } 221 | 222 | type elRequest struct { 223 | url string 224 | by By 225 | method string 226 | callingMethod string 227 | } 228 | 229 | type stateResponse struct { 230 | State string `json:"state"` 231 | } 232 | 233 | type valueResponse struct { 234 | State string `json:"state"` 235 | Value string `json:"value"` 236 | } 237 | 238 | type by struct { 239 | t string 240 | value interface{} 241 | } 242 | 243 | func (b by) Type() string { 244 | return b.t 245 | } 246 | 247 | func (b by) Value() interface{} { 248 | return b.value 249 | } 250 | -------------------------------------------------------------------------------- /remote_driver_alert.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | ) 8 | 9 | // DismissAlertResponse is the response returned from calling the DismissAlert 10 | // method. 11 | type DismissAlertResponse struct { 12 | State string 13 | } 14 | 15 | // AcceptAlertResponse is the response returned from calling the AcceptAlert 16 | // method. 17 | type AcceptAlertResponse struct { 18 | State string 19 | } 20 | 21 | // AlertTextResponse is the response returned from calling the AlertText 22 | // method. 23 | type AlertTextResponse struct { 24 | State string 25 | Text string 26 | } 27 | 28 | // SendAlertTextResponse is the response returned from calling the 29 | // SendAlertText method. 30 | type SendAlertTextResponse struct { 31 | State string 32 | } 33 | 34 | func (s *seleniumWebDriver) DismissAlert() (*DismissAlertResponse, error) { 35 | if len(s.sessionID) == 0 { 36 | return nil, newSessionIDError("DismissAlert") 37 | } 38 | 39 | var err error 40 | 41 | url := fmt.Sprintf("%s/session/%s/alert/dismiss", s.seleniumURL, s.sessionID) 42 | 43 | resp, err := s.stateRequest(&request{ 44 | url: url, 45 | method: "POST", 46 | body: nil, 47 | callingMethod: "DismissAlert", 48 | }) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | return &DismissAlertResponse{State: resp.State}, nil 54 | } 55 | 56 | func (s *seleniumWebDriver) AcceptAlert() (*AcceptAlertResponse, error) { 57 | if len(s.sessionID) == 0 { 58 | return nil, newSessionIDError("AcceptAlert") 59 | } 60 | 61 | var err error 62 | 63 | url := fmt.Sprintf("%s/session/%s/alert/accept", s.seleniumURL, s.sessionID) 64 | 65 | resp, err := s.stateRequest(&request{ 66 | url: url, 67 | method: "POST", 68 | body: nil, 69 | callingMethod: "AcceptAlert", 70 | }) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | return &AcceptAlertResponse{State: resp.State}, nil 76 | } 77 | 78 | func (s *seleniumWebDriver) AlertText() (*AlertTextResponse, error) { 79 | if len(s.sessionID) == 0 { 80 | return nil, newSessionIDError("AlertTextResponse") 81 | } 82 | 83 | var err error 84 | 85 | url := fmt.Sprintf("%s/session/%s/alert/text", s.seleniumURL, s.sessionID) 86 | 87 | resp, err := s.valueRequest(&request{ 88 | url: url, 89 | method: "GET", 90 | body: nil, 91 | callingMethod: "AlertText", 92 | }) 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | return &AlertTextResponse{State: resp.State, Text: resp.Value}, nil 98 | } 99 | 100 | func (s *seleniumWebDriver) SendAlertText(text string) (*SendAlertTextResponse, error) { 101 | if len(s.sessionID) == 0 { 102 | return nil, newSessionIDError("SendAlertText") 103 | } 104 | 105 | var err error 106 | 107 | url := fmt.Sprintf("%s/session/%s/alert/text", s.seleniumURL, s.sessionID) 108 | 109 | b := map[string]string{ 110 | "text": text, 111 | } 112 | json, err := json.Marshal(b) 113 | if err != nil { 114 | return nil, newMarshallingError(err, "SendAlertText", b) 115 | } 116 | 117 | body := bytes.NewReader(json) 118 | resp, err := s.valueRequest(&request{ 119 | url: url, 120 | method: "POST", 121 | body: body, 122 | callingMethod: "SendAlertText", 123 | }) 124 | if err != nil { 125 | return nil, err 126 | } 127 | 128 | return &SendAlertTextResponse{State: resp.State}, nil 129 | } 130 | -------------------------------------------------------------------------------- /remote_driver_alert_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | /* 9 | DismissAlert() Tests 10 | */ 11 | 12 | func Test_AlertDismissAlert_InvalidSessionIdResultsInError(t *testing.T) { 13 | api := &testableAPIService{ 14 | jsonToReturn: "", 15 | errorToReturn: nil, 16 | } 17 | 18 | d := setUpDriver(setUpDefaultCaps(), api) 19 | 20 | _, err := d.DismissAlert() 21 | if err == nil || !IsSessionIDError(err) { 22 | t.Errorf(sessionIDErrorText) 23 | } 24 | } 25 | 26 | func Test_AlertDismissAlert_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 27 | api := &testableAPIService{ 28 | jsonToReturn: "", 29 | errorToReturn: errors.New("An error :<"), 30 | } 31 | 32 | d := setUpDriver(setUpDefaultCaps(), api) 33 | d.sessionID = "12345" 34 | 35 | _, err := d.DismissAlert() 36 | if err == nil || !IsCommunicationError(err) { 37 | t.Errorf(apiCommunicationErrorText) 38 | } 39 | } 40 | 41 | func Test_AlertDismissAlert_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 42 | api := &testableAPIService{ 43 | jsonToReturn: "Invalid JSON!", 44 | errorToReturn: nil, 45 | } 46 | 47 | d := setUpDriver(setUpDefaultCaps(), api) 48 | d.sessionID = "12345" 49 | 50 | _, err := d.DismissAlert() 51 | if err == nil || !IsUnmarshallingError(err) { 52 | t.Errorf(unmarshallingErrorText) 53 | } 54 | } 55 | 56 | func Test_AlertDismissAlert_CorrectResponseIsReturned(t *testing.T) { 57 | api := &testableAPIService{ 58 | jsonToReturn: `{ 59 | "state": "success", 60 | "value": "8" 61 | }`, 62 | errorToReturn: nil, 63 | } 64 | 65 | d := setUpDriver(setUpDefaultCaps(), api) 66 | d.sessionID = "12345" 67 | 68 | resp, err := d.DismissAlert() 69 | if err != nil || resp.State != "success" { 70 | t.Errorf(correctResponseErrorText) 71 | } 72 | } 73 | 74 | /* 75 | AcceptAlert() Tests 76 | */ 77 | 78 | func Test_AlertAcceptAlert_InvalidSessionIdResultsInError(t *testing.T) { 79 | api := &testableAPIService{ 80 | jsonToReturn: "", 81 | errorToReturn: nil, 82 | } 83 | 84 | d := setUpDriver(setUpDefaultCaps(), api) 85 | 86 | _, err := d.AcceptAlert() 87 | if err == nil || !IsSessionIDError(err) { 88 | t.Errorf(sessionIDErrorText) 89 | } 90 | } 91 | 92 | func Test_AlertAcceptAlert_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 93 | api := &testableAPIService{ 94 | jsonToReturn: "", 95 | errorToReturn: errors.New("An error :<"), 96 | } 97 | 98 | d := setUpDriver(setUpDefaultCaps(), api) 99 | d.sessionID = "12345" 100 | 101 | _, err := d.AcceptAlert() 102 | if err == nil || !IsCommunicationError(err) { 103 | t.Errorf(apiCommunicationErrorText) 104 | } 105 | } 106 | 107 | func Test_AlertAcceptAlert_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 108 | api := &testableAPIService{ 109 | jsonToReturn: "Invalid JSON!", 110 | errorToReturn: nil, 111 | } 112 | 113 | d := setUpDriver(setUpDefaultCaps(), api) 114 | d.sessionID = "12345" 115 | 116 | _, err := d.AcceptAlert() 117 | if err == nil || !IsUnmarshallingError(err) { 118 | t.Errorf(unmarshallingErrorText) 119 | } 120 | } 121 | 122 | func Test_AlertAcceptAlert_CorrectResponseIsReturned(t *testing.T) { 123 | api := &testableAPIService{ 124 | jsonToReturn: `{ 125 | "state": "success", 126 | "value": "8" 127 | }`, 128 | errorToReturn: nil, 129 | } 130 | 131 | d := setUpDriver(setUpDefaultCaps(), api) 132 | d.sessionID = "12345" 133 | 134 | resp, err := d.AcceptAlert() 135 | if err != nil || resp.State != "success" { 136 | t.Errorf(correctResponseErrorText) 137 | } 138 | } 139 | 140 | /* 141 | AlertText() Tests 142 | */ 143 | 144 | func Test_AlertAlertText_InvalidSessionIdResultsInError(t *testing.T) { 145 | api := &testableAPIService{ 146 | jsonToReturn: "", 147 | errorToReturn: nil, 148 | } 149 | 150 | d := setUpDriver(setUpDefaultCaps(), api) 151 | 152 | _, err := d.AlertText() 153 | if err == nil || !IsSessionIDError(err) { 154 | t.Errorf(sessionIDErrorText) 155 | } 156 | } 157 | 158 | func Test_AlertAlertText_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 159 | api := &testableAPIService{ 160 | jsonToReturn: "", 161 | errorToReturn: errors.New("An error :<"), 162 | } 163 | 164 | d := setUpDriver(setUpDefaultCaps(), api) 165 | d.sessionID = "12345" 166 | 167 | _, err := d.AlertText() 168 | if err == nil || !IsCommunicationError(err) { 169 | t.Errorf(apiCommunicationErrorText) 170 | } 171 | } 172 | 173 | func Test_AlertAlertText_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 174 | api := &testableAPIService{ 175 | jsonToReturn: "Invalid JSON!", 176 | errorToReturn: nil, 177 | } 178 | 179 | d := setUpDriver(setUpDefaultCaps(), api) 180 | d.sessionID = "12345" 181 | 182 | _, err := d.AlertText() 183 | if err == nil || !IsUnmarshallingError(err) { 184 | t.Errorf(unmarshallingErrorText) 185 | } 186 | } 187 | 188 | func Test_AlertAlertText_CorrectResponseIsReturned(t *testing.T) { 189 | api := &testableAPIService{ 190 | jsonToReturn: `{ 191 | "state": "success", 192 | "value": "this is text" 193 | }`, 194 | errorToReturn: nil, 195 | } 196 | 197 | d := setUpDriver(setUpDefaultCaps(), api) 198 | d.sessionID = "12345" 199 | 200 | resp, err := d.AlertText() 201 | if err != nil || resp.State != "success" || resp.Text != "this is text" { 202 | t.Errorf(correctResponseErrorText) 203 | } 204 | } 205 | 206 | /* 207 | SendAlertText() Tests 208 | */ 209 | func Test_AlertSendAlertText_InvalidSessionIdResultsInError(t *testing.T) { 210 | api := &testableAPIService{ 211 | jsonToReturn: "", 212 | errorToReturn: nil, 213 | } 214 | 215 | d := setUpDriver(setUpDefaultCaps(), api) 216 | 217 | _, err := d.SendAlertText("test") 218 | if err == nil || !IsSessionIDError(err) { 219 | t.Errorf(sessionIDErrorText) 220 | } 221 | } 222 | 223 | func Test_AlertSendAlertText_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 224 | api := &testableAPIService{ 225 | jsonToReturn: "", 226 | errorToReturn: errors.New("An error :<"), 227 | } 228 | 229 | d := setUpDriver(setUpDefaultCaps(), api) 230 | d.sessionID = "12345" 231 | 232 | _, err := d.SendAlertText("test") 233 | if err == nil || !IsCommunicationError(err) { 234 | t.Errorf(apiCommunicationErrorText) 235 | } 236 | } 237 | 238 | func Test_AlertSendAlertText_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 239 | api := &testableAPIService{ 240 | jsonToReturn: "Invalid JSON!", 241 | errorToReturn: nil, 242 | } 243 | 244 | d := setUpDriver(setUpDefaultCaps(), api) 245 | d.sessionID = "12345" 246 | 247 | _, err := d.SendAlertText("test") 248 | if err == nil || !IsUnmarshallingError(err) { 249 | t.Errorf(unmarshallingErrorText) 250 | } 251 | } 252 | 253 | func Test_AlertSendAlertText_CorrectResponseIsReturned(t *testing.T) { 254 | api := &testableAPIService{ 255 | jsonToReturn: `{ 256 | "state": "success" 257 | }`, 258 | errorToReturn: nil, 259 | } 260 | 261 | d := setUpDriver(setUpDefaultCaps(), api) 262 | d.sessionID = "12345" 263 | 264 | resp, err := d.SendAlertText("test") 265 | if err != nil || resp.State != "success" { 266 | t.Errorf(correctResponseErrorText) 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /remote_driver_command.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | ) 9 | 10 | // WindowHandleResponse is the response returned from the WindowHandle() method. 11 | // The handle is the current active window. Should you switch windows, 12 | // any value returned prior to that call will be invalid. 13 | type WindowHandleResponse struct { 14 | State string 15 | Handle string 16 | } 17 | 18 | // CloseWindowResponse is the response returned from the CloseWindow() method. 19 | // As per the W3C specification, it yields all of the available window handles 20 | // minus the active one that closes as a result of the CloseWindow() call. 21 | type CloseWindowResponse struct { 22 | State string `json:"state"` 23 | Handles []string `json:"value"` 24 | } 25 | 26 | // SwitchToWindowResponse is the response returned from the SwitchToWindow() 27 | // method. You can verify that this result is correct by calling the 28 | // WindowHandle() method. The two should match. 29 | type SwitchToWindowResponse struct { 30 | } 31 | 32 | // WindowHandlesResponse is the response returned from the WindowHandles() 33 | // method. This is essentially an array of available window handlers that 34 | // aren't necessarily active. 35 | type WindowHandlesResponse struct { 36 | State string `json:"state"` 37 | Handles []string `json:"value"` 38 | } 39 | 40 | // SwitchToFrameResponse is the response returned from the SwitchToFrame() 41 | // method. For now, according to the specification, it only returns a state. 42 | type SwitchToFrameResponse struct { 43 | State string 44 | } 45 | 46 | // SwitchToParentFrameResponse represents the response from attempting to 47 | // switch the top level browsing context to the parent of the current top level 48 | // browsing context. 49 | type SwitchToParentFrameResponse struct { 50 | State string 51 | } 52 | 53 | // WindowSizeResponse is the response returned from calling the WindowSize 54 | // method. The definitions are in CSS pixels. 55 | type WindowSizeResponse struct { 56 | State string `json:"state"` 57 | Dimensions Dimensions `json:"value"` 58 | } 59 | 60 | // Dimensions is a type that is both returned and accept by functions. It is 61 | // usually only used for the window size components. 62 | type Dimensions struct { 63 | Width uint `json:"width"` 64 | Height uint `json:"height"` 65 | } 66 | 67 | // SetWindowSizeResponse is the response that is returned from setting the 68 | // window size of the current top level browsing context. 69 | type SetWindowSizeResponse struct { 70 | State string 71 | } 72 | 73 | // MaximizeWindowResponse is the response that is returned from increasing the 74 | // browser to match the viewport. 75 | type MaximizeWindowResponse struct { 76 | State string 77 | } 78 | 79 | func (s *seleniumWebDriver) WindowHandle() (*WindowHandleResponse, error) { 80 | if len(s.sessionID) == 0 { 81 | return nil, newSessionIDError("WindowHandle") 82 | } 83 | 84 | var response WindowHandleResponse 85 | var err error 86 | 87 | url := fmt.Sprintf("%s/session/%s/window", s.seleniumURL, s.sessionID) 88 | 89 | resp, err := s.valueRequest(&request{ 90 | url: url, 91 | method: "GET", 92 | body: nil, 93 | callingMethod: "WindowHandle", 94 | }) 95 | if err != nil { 96 | return nil, err 97 | } 98 | 99 | response = WindowHandleResponse{ 100 | State: resp.State, 101 | Handle: resp.Value, 102 | } 103 | 104 | return &response, nil 105 | } 106 | 107 | func (s *seleniumWebDriver) CloseWindow() (*CloseWindowResponse, error) { 108 | if len(s.sessionID) == 0 { 109 | return nil, newSessionIDError("CloseWindow") 110 | } 111 | 112 | var response CloseWindowResponse 113 | var err error 114 | 115 | url := fmt.Sprintf("%s/session/%s/window", s.seleniumURL, s.sessionID) 116 | 117 | resp, err := s.apiService.performRequest(url, "DELETE", nil) 118 | if err != nil { 119 | return nil, newCommunicationError(err, "CloseWindow", url, resp) 120 | } 121 | 122 | err = json.Unmarshal(resp, &response) 123 | if err != nil { 124 | return nil, newUnmarshallingError(err, "CloseWindow", string(resp)) 125 | } 126 | 127 | return &response, nil 128 | } 129 | 130 | func (s *seleniumWebDriver) SwitchToWindow(handle string) (*SwitchToWindowResponse, error) { 131 | return nil, nil 132 | } 133 | 134 | func (s *seleniumWebDriver) WindowHandles() (*WindowHandlesResponse, error) { 135 | if len(s.sessionID) == 0 { 136 | return nil, newSessionIDError("WindowHandles") 137 | } 138 | 139 | var response WindowHandlesResponse 140 | var err error 141 | 142 | url := fmt.Sprintf("%s/session/%s/window/handles", s.seleniumURL, s.sessionID) 143 | 144 | resp, err := s.apiService.performRequest(url, "GET", nil) 145 | if err != nil { 146 | return nil, newCommunicationError(err, "WindowHandles", url, resp) 147 | } 148 | 149 | err = json.Unmarshal(resp, &response) 150 | if err != nil { 151 | return nil, newUnmarshallingError(err, "WindowHandles", string(resp)) 152 | } 153 | 154 | return &response, nil 155 | } 156 | 157 | func (s *seleniumWebDriver) SwitchToFrame(by By) (*SwitchToFrameResponse, error) { 158 | if len(s.sessionID) == 0 { 159 | return nil, newSessionIDError("SwitchToFrame") 160 | } 161 | if by == nil || (by.Type() != "index") { 162 | return nil, errors.New("switchtoframe: invalid by argument") 163 | } 164 | 165 | var err error 166 | 167 | url := fmt.Sprintf("%s/session/%s/frame", s.seleniumURL, s.sessionID) 168 | 169 | params := map[string]interface{}{ 170 | "id": by.Value(), 171 | } 172 | requestJSON, err := json.Marshal(params) 173 | if err != nil { 174 | return nil, newMarshallingError(err, "SwitchToFrame", params) 175 | } 176 | 177 | body := bytes.NewReader(requestJSON) 178 | resp, err := s.stateRequest(&request{ 179 | url: url, 180 | method: "POST", 181 | body: body, 182 | callingMethod: "SwitchToFrame", 183 | }) 184 | if err != nil { 185 | return nil, err 186 | } 187 | 188 | return &SwitchToFrameResponse{State: resp.State}, nil 189 | } 190 | 191 | func (s *seleniumWebDriver) SwitchToParentFrame() (*SwitchToParentFrameResponse, error) { 192 | if len(s.sessionID) == 0 { 193 | return nil, newSessionIDError("SwitchToParentFrame") 194 | } 195 | 196 | var err error 197 | 198 | url := fmt.Sprintf("%s/session/%s/frame/parent", s.seleniumURL, s.sessionID) 199 | 200 | resp, err := s.stateRequest(&request{ 201 | url: url, 202 | method: "POST", 203 | body: nil, 204 | callingMethod: "SwitchToParentFrame", 205 | }) 206 | if err != nil { 207 | return nil, err 208 | } 209 | 210 | return &SwitchToParentFrameResponse{State: resp.State}, nil 211 | } 212 | 213 | func (s *seleniumWebDriver) WindowSize() (*WindowSizeResponse, error) { 214 | if len(s.sessionID) == 0 { 215 | return nil, newSessionIDError("WindowSize") 216 | } 217 | 218 | var response WindowSizeResponse 219 | var err error 220 | 221 | url := fmt.Sprintf("%s/session/%s/window/size", s.seleniumURL, s.sessionID) 222 | 223 | resp, err := s.apiService.performRequest(url, "GET", nil) 224 | if err != nil { 225 | return nil, newCommunicationError(err, "WindowSize", url, nil) 226 | } 227 | 228 | err = json.Unmarshal(resp, &response) 229 | if err != nil { 230 | return nil, newUnmarshallingError(err, "WindowSize", string(resp)) 231 | } 232 | 233 | return &response, nil 234 | } 235 | 236 | func (s *seleniumWebDriver) SetWindowSize(dimension *Dimensions) (*SetWindowSizeResponse, error) { 237 | if dimension == nil { 238 | return nil, errors.New("setwindowsize: invalid dimension argument") 239 | } else if len(s.sessionID) == 0 { 240 | return nil, newSessionIDError("SetWindowSize") 241 | } 242 | 243 | var err error 244 | 245 | url := fmt.Sprintf("%s/session/%s/window/size", s.seleniumURL, s.sessionID) 246 | 247 | body := map[string]uint{ 248 | "width": dimension.Width, 249 | "height": dimension.Height, 250 | } 251 | json, err := json.Marshal(body) 252 | if err != nil { 253 | return nil, newMarshallingError(err, "SetWindowSize", body) 254 | } 255 | 256 | jsonBytes := bytes.NewReader(json) 257 | resp, err := s.stateRequest(&request{ 258 | url: url, 259 | method: "POST", 260 | body: jsonBytes, 261 | callingMethod: "SetWindowSize", 262 | }) 263 | if err != nil { 264 | return nil, err 265 | } 266 | 267 | return &SetWindowSizeResponse{State: resp.State}, nil 268 | } 269 | 270 | func (s *seleniumWebDriver) MaximizeWindow() (*MaximizeWindowResponse, error) { 271 | if len(s.sessionID) == 0 { 272 | return nil, newSessionIDError("MaximizeWindow") 273 | } 274 | 275 | var err error 276 | 277 | url := fmt.Sprintf("%s/session/%s/window/maximize", s.seleniumURL, s.sessionID) 278 | 279 | resp, err := s.stateRequest(&request{ 280 | url: url, 281 | method: "POST", 282 | body: nil, 283 | callingMethod: "MaximizeWindow", 284 | }) 285 | if err != nil { 286 | return nil, err 287 | } 288 | 289 | return &MaximizeWindowResponse{State: resp.State}, nil 290 | } 291 | -------------------------------------------------------------------------------- /remote_driver_command_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | /* 9 | WindowHandle() Tests 10 | */ 11 | 12 | func Test_CommandWindowHandle_InvalidSessionIdResultsInError(t *testing.T) { 13 | api := &testableAPIService{ 14 | jsonToReturn: "", 15 | errorToReturn: nil, 16 | } 17 | 18 | d := setUpDriver(setUpDefaultCaps(), api) 19 | _, err := d.WindowHandle() 20 | if err == nil || !IsSessionIDError(err) { 21 | t.Errorf(sessionIDErrorText) 22 | } 23 | } 24 | 25 | func Test_CommandWindowHandle_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 26 | api := &testableAPIService{ 27 | jsonToReturn: "", 28 | errorToReturn: errors.New("An error :<"), 29 | } 30 | 31 | d := setUpDriver(setUpDefaultCaps(), api) 32 | d.sessionID = "12345" 33 | _, err := d.WindowHandle() 34 | if err == nil || !IsCommunicationError(err) { 35 | t.Errorf(apiCommunicationErrorText) 36 | } 37 | } 38 | 39 | func Test_CommandWindowHandle_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 40 | api := &testableAPIService{ 41 | jsonToReturn: "Invalid JSON!", 42 | errorToReturn: nil, 43 | } 44 | 45 | d := setUpDriver(setUpDefaultCaps(), api) 46 | d.sessionID = "12345" 47 | _, err := d.WindowHandle() 48 | if err == nil || !IsUnmarshallingError(err) { 49 | t.Errorf(unmarshallingErrorText) 50 | } 51 | } 52 | 53 | func Test_CommandWindowHandle_CorrectResponseIsReturned(t *testing.T) { 54 | api := &testableAPIService{ 55 | jsonToReturn: `{ 56 | "state": "success", 57 | "value": "8" 58 | }`, 59 | errorToReturn: nil, 60 | } 61 | 62 | d := setUpDriver(setUpDefaultCaps(), api) 63 | d.sessionID = "12345" 64 | resp, err := d.WindowHandle() 65 | if err != nil || resp.State != "success" || resp.Handle != "8" { 66 | t.Errorf(correctResponseErrorText) 67 | } 68 | } 69 | 70 | /* 71 | CloseWindow() Tests 72 | */ 73 | func Test_CommandCloseWindow_InvalidSessionIdResultsInError(t *testing.T) { 74 | api := &testableAPIService{ 75 | jsonToReturn: "", 76 | errorToReturn: nil, 77 | } 78 | 79 | d := setUpDriver(setUpDefaultCaps(), api) 80 | _, err := d.CloseWindow() 81 | if err == nil || !IsSessionIDError(err) { 82 | t.Errorf(sessionIDErrorText) 83 | } 84 | } 85 | 86 | func Test_CommandCloseWindow_CommunicationErrorIsReturned(t *testing.T) { 87 | api := &testableAPIService{ 88 | jsonToReturn: "", 89 | errorToReturn: errors.New("An error"), 90 | } 91 | 92 | d := setUpDriver(setUpDefaultCaps(), api) 93 | d.sessionID = "12345" 94 | _, err := d.CloseWindow() 95 | if err == nil || !IsCommunicationError(err) { 96 | t.Errorf(apiCommunicationErrorText) 97 | } 98 | 99 | } 100 | 101 | func Test_CommandCloseWindow_UnmarshallingErrorIsReturned(t *testing.T) { 102 | api := &testableAPIService{ 103 | jsonToReturn: "Invalid JSON!", 104 | errorToReturn: nil, 105 | } 106 | 107 | d := setUpDriver(setUpDefaultCaps(), api) 108 | d.sessionID = "12345" 109 | _, err := d.CloseWindow() 110 | if err == nil || !IsUnmarshallingError(err) { 111 | t.Errorf(unmarshallingErrorText) 112 | } 113 | } 114 | 115 | /* 116 | SwitchToWindow() Tests 117 | */ 118 | 119 | /* 120 | WindowHandles() Tests 121 | */ 122 | func Test_CommandWindowHandles_InvalidSessionIdResultsInError(t *testing.T) { 123 | api := &testableAPIService{ 124 | jsonToReturn: "", 125 | errorToReturn: nil, 126 | } 127 | 128 | d := setUpDriver(setUpDefaultCaps(), api) 129 | _, err := d.WindowHandles() 130 | if err == nil || !IsSessionIDError(err) { 131 | t.Errorf(sessionIDErrorText) 132 | } 133 | } 134 | 135 | func Test_CommandWindowHandles_CommunicationErrorIsReturned(t *testing.T) { 136 | api := &testableAPIService{ 137 | jsonToReturn: "", 138 | errorToReturn: errors.New("An error"), 139 | } 140 | 141 | d := setUpDriver(setUpDefaultCaps(), api) 142 | d.sessionID = "12345" 143 | _, err := d.WindowHandles() 144 | if err == nil || !IsCommunicationError(err) { 145 | t.Errorf(apiCommunicationErrorText) 146 | } 147 | } 148 | 149 | func Test_CommandWindowHandles_UnmarshallingErrorIsReturned(t *testing.T) { 150 | api := &testableAPIService{ 151 | jsonToReturn: "Invalid JSON!", 152 | errorToReturn: nil, 153 | } 154 | 155 | d := setUpDriver(setUpDefaultCaps(), api) 156 | d.sessionID = "12345" 157 | _, err := d.WindowHandles() 158 | if err == nil || !IsUnmarshallingError(err) { 159 | t.Errorf(unmarshallingErrorText) 160 | } 161 | } 162 | 163 | func Test_CommandWindowHandles_SingleResultCanBeReturned(t *testing.T) { 164 | api := &testableAPIService{ 165 | jsonToReturn: `{ 166 | "state": "success", 167 | "value": [ 168 | "8" 169 | ] 170 | }`, 171 | errorToReturn: nil, 172 | } 173 | 174 | d := setUpDriver(setUpDefaultCaps(), api) 175 | d.sessionID = "12345" 176 | resp, err := d.WindowHandles() 177 | if err != nil || resp.State != "success" || resp.Handles[0] != "8" { 178 | t.Errorf(correctResponseErrorText) 179 | } 180 | } 181 | 182 | func Test_CommandWindowHandles_MultipleResultsCanBeReturned(t *testing.T) { 183 | api := &testableAPIService{ 184 | jsonToReturn: `{ 185 | "state": "success", 186 | "value": [ 187 | "8", 188 | "9" 189 | ] 190 | }`, 191 | errorToReturn: nil, 192 | } 193 | 194 | d := setUpDriver(setUpDefaultCaps(), api) 195 | d.sessionID = "12345" 196 | resp, err := d.WindowHandles() 197 | if err != nil || resp.State != "success" || resp.Handles[0] != "8" || resp.Handles[1] != "9" { 198 | t.Errorf(correctResponseErrorText) 199 | } 200 | } 201 | 202 | /* 203 | SwitchToFrame Tests 204 | */ 205 | func Test_CommandSwitchToFrame_InvalidSessionIdResultsInError(t *testing.T) { 206 | api := &testableAPIService{ 207 | jsonToReturn: "", 208 | errorToReturn: nil, 209 | } 210 | 211 | d := setUpDriver(setUpDefaultCaps(), api) 212 | _, err := d.SwitchToFrame(nil) 213 | if err == nil || !IsSessionIDError(err) { 214 | t.Errorf(sessionIDErrorText) 215 | } 216 | } 217 | 218 | func Test_CommandSwitchToFrame_InvalidByResultsInError(t *testing.T) { 219 | api := &testableAPIService{ 220 | jsonToReturn: "", 221 | errorToReturn: nil, 222 | } 223 | 224 | d := setUpDriver(setUpDefaultCaps(), api) 225 | d.sessionID = "12345" 226 | 227 | invalidBys := []By{ 228 | nil, 229 | ByCSSSelector("test"), 230 | } 231 | 232 | for _, i := range invalidBys { 233 | _, err := d.SwitchToFrame(i) 234 | if err == nil { 235 | t.Errorf(argumentErrorText) 236 | } 237 | } 238 | } 239 | 240 | func Test_CommandSwitchToFrame_APICommunicationErrorIsReturned(t *testing.T) { 241 | api := &testableAPIService{ 242 | jsonToReturn: "", 243 | errorToReturn: errors.New("An error"), 244 | } 245 | 246 | d := setUpDriver(setUpDefaultCaps(), api) 247 | d.sessionID = "12345" 248 | 249 | _, err := d.SwitchToFrame(ByIndex(1)) 250 | if err == nil || !IsCommunicationError(err) { 251 | t.Errorf(apiCommunicationErrorText) 252 | } 253 | } 254 | 255 | func Test_CommandSwitchToFrame_CorrectResponseIsReturned(t *testing.T) { 256 | api := &testableAPIService{ 257 | jsonToReturn: `{ 258 | "state": "success" 259 | }`, 260 | errorToReturn: nil, 261 | } 262 | 263 | d := setUpDriver(setUpDefaultCaps(), api) 264 | d.sessionID = "12345" 265 | 266 | resp, err := d.SwitchToFrame(ByIndex(32)) 267 | if err != nil || resp.State != "success" { 268 | t.Errorf(correctResponseErrorText) 269 | } 270 | } 271 | 272 | /* 273 | SwitchToParentFrame tests 274 | */ 275 | func Test_CommandSwitchToParentFrame_InvalidSessionIDResultsInError(t *testing.T) { 276 | api := &testableAPIService{ 277 | jsonToReturn: "", 278 | errorToReturn: nil, 279 | } 280 | 281 | d := setUpDriver(setUpDefaultCaps(), api) 282 | _, err := d.SwitchToFrame(nil) 283 | if err == nil || !IsSessionIDError(err) { 284 | t.Errorf(sessionIDErrorText) 285 | } 286 | } 287 | 288 | func Test_CommandSwitchToParentFrame_ApiCommunicationErrorIsReturned(t *testing.T) { 289 | api := &testableAPIService{ 290 | jsonToReturn: "", 291 | errorToReturn: errors.New("An error"), 292 | } 293 | 294 | d := setUpDriver(setUpDefaultCaps(), api) 295 | d.sessionID = "12345" 296 | 297 | _, err := d.SwitchToParentFrame() 298 | if err == nil || !IsCommunicationError(err) { 299 | t.Errorf(apiCommunicationErrorText) 300 | } 301 | } 302 | 303 | func Test_CommandSwitchToParentFrame_UnmarshallingErrorIsReturned(t *testing.T) { 304 | api := &testableAPIService{ 305 | jsonToReturn: "Invalid JSON", 306 | errorToReturn: nil, 307 | } 308 | 309 | d := setUpDriver(setUpDefaultCaps(), api) 310 | d.sessionID = "12345" 311 | 312 | _, err := d.SwitchToParentFrame() 313 | if err == nil || !IsUnmarshallingError(err) { 314 | t.Errorf(unmarshallingErrorText) 315 | } 316 | } 317 | 318 | func Test_CommandSwitchToParentFrame_CorrectResponseCanBeReturned(t *testing.T) { 319 | api := &testableAPIService{ 320 | jsonToReturn: `{ 321 | "state": "success" 322 | }`, 323 | errorToReturn: nil, 324 | } 325 | 326 | d := setUpDriver(setUpDefaultCaps(), api) 327 | d.sessionID = "12345" 328 | 329 | resp, err := d.SwitchToParentFrame() 330 | if err != nil || resp.State != "success" { 331 | t.Errorf(correctResponseErrorText) 332 | } 333 | } 334 | 335 | /* 336 | WindowSize tests 337 | */ 338 | func Test_CommandWindowSize_InvalidSessionIDResultsInAnError(t *testing.T) { 339 | api := &testableAPIService{ 340 | jsonToReturn: "", 341 | errorToReturn: nil, 342 | } 343 | 344 | d := setUpDriver(setUpDefaultCaps(), api) 345 | _, err := d.WindowSize() 346 | if err == nil || !IsSessionIDError(err) { 347 | t.Errorf(sessionIDErrorText) 348 | } 349 | } 350 | 351 | func Test_CommandWindowSize_CommunicationErrorIsReturned(t *testing.T) { 352 | api := &testableAPIService{ 353 | jsonToReturn: "", 354 | errorToReturn: errors.New("An error"), 355 | } 356 | 357 | d := setUpDriver(setUpDefaultCaps(), api) 358 | d.sessionID = "12345" 359 | 360 | _, err := d.WindowSize() 361 | if err == nil || !IsCommunicationError(err) { 362 | t.Errorf(apiCommunicationErrorText) 363 | } 364 | } 365 | 366 | func Test_CommandWindowSize_UnmarshallingErrorIsReturned(t *testing.T) { 367 | api := &testableAPIService{ 368 | jsonToReturn: "Invalid JSON", 369 | errorToReturn: nil, 370 | } 371 | 372 | d := setUpDriver(setUpDefaultCaps(), api) 373 | d.sessionID = "12345" 374 | 375 | _, err := d.WindowSize() 376 | if err == nil || !IsUnmarshallingError(err) { 377 | t.Errorf(unmarshallingErrorText) 378 | } 379 | } 380 | 381 | func Test_CommandWindowSize_CorrectResultIsReturned(t *testing.T) { 382 | api := &testableAPIService{ 383 | jsonToReturn: `{ 384 | "state": "success", 385 | "value": { 386 | "width": 800, 387 | "height": 600 388 | } 389 | }`, 390 | errorToReturn: nil, 391 | } 392 | 393 | d := setUpDriver(setUpDefaultCaps(), api) 394 | d.sessionID = "12345" 395 | 396 | resp, err := d.WindowSize() 397 | if err != nil || resp.State != "success" || resp.Dimensions.Width == 0 || resp.Dimensions.Height == 0 { 398 | t.Errorf(correctResponseErrorText) 399 | } 400 | } 401 | 402 | /* 403 | SetWindowSize tests 404 | */ 405 | 406 | func Test_CommandSetWindowSize_NullDimensionResultsInError(t *testing.T) { 407 | api := &testableAPIService{ 408 | jsonToReturn: "", 409 | errorToReturn: nil, 410 | } 411 | 412 | d := setUpDriver(setUpDefaultCaps(), api) 413 | 414 | _, err := d.SetWindowSize(nil) 415 | if err == nil { 416 | t.Errorf(argumentErrorText) 417 | } 418 | } 419 | 420 | func Test_CommandSetWindowSize_InvalidSessionIDResultsInError(t *testing.T) { 421 | api := &testableAPIService{ 422 | jsonToReturn: "", 423 | errorToReturn: nil, 424 | } 425 | 426 | d := setUpDriver(setUpDefaultCaps(), api) 427 | 428 | dimensions := &Dimensions{ 429 | Width: 830, 430 | Height: 255, 431 | } 432 | 433 | _, err := d.SetWindowSize(dimensions) 434 | if err == nil || !IsSessionIDError(err) { 435 | t.Errorf(sessionIDErrorText) 436 | } 437 | } 438 | 439 | func Test_CommandSetWindowSize_CommunicationErrorIsReturned(t *testing.T) { 440 | api := &testableAPIService{ 441 | jsonToReturn: "", 442 | errorToReturn: errors.New("An error"), 443 | } 444 | 445 | d := setUpDriver(setUpDefaultCaps(), api) 446 | d.sessionID = "12345" 447 | 448 | dimensions := &Dimensions{ 449 | Width: 830, 450 | Height: 255, 451 | } 452 | 453 | _, err := d.SetWindowSize(dimensions) 454 | if err == nil || !IsCommunicationError(err) { 455 | t.Errorf(apiCommunicationErrorText) 456 | } 457 | } 458 | 459 | func Test_CommandSetWindowSize_UnmarshallingErrorIsReturned(t *testing.T) { 460 | api := &testableAPIService{ 461 | jsonToReturn: "Invalid JSON", 462 | errorToReturn: nil, 463 | } 464 | 465 | d := setUpDriver(setUpDefaultCaps(), api) 466 | d.sessionID = "12345" 467 | 468 | dimensions := &Dimensions{ 469 | Width: 830, 470 | Height: 255, 471 | } 472 | 473 | _, err := d.SetWindowSize(dimensions) 474 | if err == nil || !IsUnmarshallingError(err) { 475 | t.Errorf(unmarshallingErrorText) 476 | } 477 | } 478 | 479 | func Test_CommandSetWindowSize_ResultIsReturnedSuccessfully(t *testing.T) { 480 | api := &testableAPIService{ 481 | jsonToReturn: `{ 482 | "state": "success" 483 | }`, 484 | errorToReturn: nil, 485 | } 486 | 487 | d := setUpDriver(setUpDefaultCaps(), api) 488 | d.sessionID = "12345" 489 | 490 | dimensions := &Dimensions{ 491 | Width: 830, 492 | Height: 255, 493 | } 494 | 495 | resp, err := d.SetWindowSize(dimensions) 496 | if err != nil || resp.State != "success" { 497 | t.Errorf(correctResponseErrorText) 498 | } 499 | } 500 | 501 | /* 502 | MaximizeWindow tests 503 | */ 504 | func Test_CommandMaximizeWindow_InvalidSessionIDResultsInError(t *testing.T) { 505 | api := &testableAPIService{ 506 | jsonToReturn: "", 507 | errorToReturn: nil, 508 | } 509 | 510 | d := setUpDriver(setUpDefaultCaps(), api) 511 | 512 | _, err := d.MaximizeWindow() 513 | if err == nil || !IsSessionIDError(err) { 514 | t.Errorf(sessionIDErrorText) 515 | } 516 | } 517 | 518 | func Test_CommandMaximizeWindow_CommunicationErrorIsReturned(t *testing.T) { 519 | api := &testableAPIService{ 520 | jsonToReturn: "", 521 | errorToReturn: errors.New("An error"), 522 | } 523 | 524 | d := setUpDriver(setUpDefaultCaps(), api) 525 | d.sessionID = "12345" 526 | 527 | _, err := d.MaximizeWindow() 528 | if err == nil || !IsCommunicationError(err) { 529 | t.Errorf(apiCommunicationErrorText) 530 | } 531 | } 532 | 533 | func Test_CommandMaximizeWindow_UnmarshallingErrorIsReturned(t *testing.T) { 534 | api := &testableAPIService{ 535 | jsonToReturn: "Invalid JSON", 536 | errorToReturn: nil, 537 | } 538 | 539 | d := setUpDriver(setUpDefaultCaps(), api) 540 | d.sessionID = "12345" 541 | 542 | _, err := d.MaximizeWindow() 543 | if err == nil || !IsUnmarshallingError(err) { 544 | t.Errorf(unmarshallingErrorText) 545 | } 546 | } 547 | 548 | func Test_CommandMaximizeWindow_ResultIsReturnedSuccessfully(t *testing.T) { 549 | api := &testableAPIService{ 550 | jsonToReturn: `{ 551 | "state": "success" 552 | }`, 553 | errorToReturn: nil, 554 | } 555 | 556 | d := setUpDriver(setUpDefaultCaps(), api) 557 | d.sessionID = "12345" 558 | 559 | resp, err := d.MaximizeWindow() 560 | if err != nil || resp.State != "success" { 561 | t.Errorf(correctResponseErrorText) 562 | } 563 | } 564 | -------------------------------------------------------------------------------- /remote_driver_cookie.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | ) 8 | 9 | // AllCookiesResponse is the response returned from the AllCookies method. 10 | type AllCookiesResponse struct { 11 | State string `json:"state"` 12 | Cookies []Cookie `json:"value"` 13 | } 14 | 15 | // CookieResponse is the response returned from the Cookie method. 16 | type CookieResponse struct { 17 | State string `json:"state"` 18 | Cookie Cookie `json:"value"` 19 | } 20 | 21 | // Cookie represents a browser cookie. 22 | type Cookie struct { 23 | Name string `json:"name"` 24 | Value string `json:"value"` 25 | Path string `json:"path"` 26 | Domain string `json:"domain"` 27 | SecureOnly bool `json:"secure"` 28 | HTTPOnly bool `json:"httpOnly"` 29 | } 30 | 31 | // AddCookieResponse is the result returned from calling the AddCookie method. 32 | type AddCookieResponse struct { 33 | State string 34 | } 35 | 36 | // DeleteCookieResponse is the result returned from calling the DeleteCookie 37 | // method. 38 | type DeleteCookieResponse struct { 39 | State string 40 | } 41 | 42 | func (s *seleniumWebDriver) AllCookies() (*AllCookiesResponse, error) { 43 | if len(s.sessionID) == 0 { 44 | return nil, newSessionIDError("AllCookies") 45 | } 46 | 47 | var response AllCookiesResponse 48 | var err error 49 | 50 | url := fmt.Sprintf("%s/session/%s/cookie", s.seleniumURL, s.sessionID) 51 | 52 | resp, err := s.apiService.performRequest(url, "GET", nil) 53 | if err != nil { 54 | return nil, newCommunicationError(err, "AllCookies", url, nil) 55 | } 56 | 57 | err = json.Unmarshal(resp, &response) 58 | if err != nil { 59 | return nil, newUnmarshallingError(err, "AllCookies", string(resp)) 60 | } 61 | 62 | return &response, nil 63 | } 64 | 65 | func (s *seleniumWebDriver) Cookie(name string) (*CookieResponse, error) { 66 | if len(s.sessionID) == 0 { 67 | return nil, newSessionIDError("Cookie") 68 | } 69 | 70 | var response CookieResponse 71 | var err error 72 | 73 | url := fmt.Sprintf("%s/session/%s/cookie/%s", s.seleniumURL, s.sessionID, name) 74 | 75 | resp, err := s.apiService.performRequest(url, "GET", nil) 76 | if err != nil { 77 | return nil, newCommunicationError(err, "Cookie", url, nil) 78 | } 79 | 80 | err = json.Unmarshal(resp, &response) 81 | if err != nil { 82 | return nil, newUnmarshallingError(err, "Cookie", string(resp)) 83 | } 84 | 85 | return &response, nil 86 | } 87 | 88 | func (s *seleniumWebDriver) AddCookie(c *Cookie) (*AddCookieResponse, error) { 89 | if len(s.sessionID) == 0 { 90 | return nil, newSessionIDError("AddCookie") 91 | } 92 | 93 | var err error 94 | 95 | url := fmt.Sprintf("%s/session/%s/cookie", s.seleniumURL, s.sessionID) 96 | 97 | j := map[string]Cookie{ 98 | "cookie": *c, 99 | } 100 | b, err := json.Marshal(j) 101 | if err != nil { 102 | return nil, newMarshallingError(err, "AddCookie", c) 103 | } 104 | 105 | body := bytes.NewReader(b) 106 | resp, err := s.stateRequest(&request{ 107 | url: url, 108 | body: body, 109 | method: "POST", 110 | callingMethod: "AddCookie", 111 | }) 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | return &AddCookieResponse{State: resp.State}, nil 117 | } 118 | 119 | func (s *seleniumWebDriver) DeleteCookie(name string) (*DeleteCookieResponse, error) { 120 | if len(s.sessionID) == 0 { 121 | return nil, newSessionIDError("DeleteCookie") 122 | } 123 | 124 | var err error 125 | 126 | url := fmt.Sprintf("%s/session/%s/cookie/%s", s.seleniumURL, s.sessionID, name) 127 | 128 | resp, err := s.stateRequest(&request{ 129 | url: url, 130 | body: nil, 131 | method: "DELETE", 132 | callingMethod: "DeleteCookie", 133 | }) 134 | if err != nil { 135 | return nil, err 136 | } 137 | 138 | return &DeleteCookieResponse{State: resp.State}, nil 139 | } 140 | -------------------------------------------------------------------------------- /remote_driver_cookie_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | /* 9 | AllCookies tests 10 | */ 11 | func Test_CookieAllCookies_InvalidSessionIdResultsInError(t *testing.T) { 12 | api := &testableAPIService{ 13 | jsonToReturn: "", 14 | errorToReturn: nil, 15 | } 16 | 17 | d := setUpDriver(setUpDefaultCaps(), api) 18 | _, err := d.AllCookies() 19 | if err == nil || !IsSessionIDError(err) { 20 | t.Errorf(sessionIDErrorText) 21 | } 22 | } 23 | 24 | func Test_CookieAllCookies_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 25 | api := &testableAPIService{ 26 | jsonToReturn: "", 27 | errorToReturn: errors.New("An error :<"), 28 | } 29 | 30 | d := setUpDriver(setUpDefaultCaps(), api) 31 | d.sessionID = "12345" 32 | 33 | _, err := d.AllCookies() 34 | if err == nil || !IsCommunicationError(err) { 35 | t.Errorf(apiCommunicationErrorText) 36 | } 37 | } 38 | 39 | func Test_CookieAllCookies_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 40 | api := &testableAPIService{ 41 | jsonToReturn: "Invalid JSON!", 42 | errorToReturn: nil, 43 | } 44 | 45 | d := setUpDriver(setUpDefaultCaps(), api) 46 | d.sessionID = "12345" 47 | 48 | _, err := d.AllCookies() 49 | if err == nil || !IsUnmarshallingError(err) { 50 | t.Errorf(unmarshallingErrorText) 51 | } 52 | } 53 | 54 | func Test_CookieAllCookies_CorrectResponseIsReturned(t *testing.T) { 55 | api := &testableAPIService{ 56 | jsonToReturn: `{ 57 | "state": "success", 58 | "value": [ 59 | { 60 | "name": "Test Cookie", 61 | "value": "Test Value", 62 | "path": "/", 63 | "domain": "www.google.com", 64 | "secure": true, 65 | "httpOnly": true, 66 | "expiry": "2016-12-25T00:00:00Z" 67 | } 68 | ] 69 | }`, 70 | errorToReturn: nil, 71 | } 72 | 73 | d := setUpDriver(setUpDefaultCaps(), api) 74 | d.sessionID = "12345" 75 | 76 | resp, err := d.AllCookies() 77 | if err != nil || resp.State != "success" || resp.Cookies[0].Name != "Test Cookie" || 78 | resp.Cookies[0].Value != "Test Value" || resp.Cookies[0].Path != "/" || 79 | resp.Cookies[0].Domain != "www.google.com" || !resp.Cookies[0].SecureOnly || 80 | !resp.Cookies[0].HTTPOnly { 81 | t.Errorf(correctResponseErrorText) 82 | } 83 | } 84 | 85 | /* 86 | Cookie tests 87 | */ 88 | func Test_CookieCookie_InvalidSessionIdResultsInError(t *testing.T) { 89 | api := &testableAPIService{ 90 | jsonToReturn: "", 91 | errorToReturn: nil, 92 | } 93 | 94 | d := setUpDriver(setUpDefaultCaps(), api) 95 | _, err := d.Cookie("test") 96 | if err == nil || !IsSessionIDError(err) { 97 | t.Errorf(sessionIDErrorText) 98 | } 99 | } 100 | 101 | func Test_CookieCookie_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 102 | api := &testableAPIService{ 103 | jsonToReturn: "", 104 | errorToReturn: errors.New("An error :<"), 105 | } 106 | 107 | d := setUpDriver(setUpDefaultCaps(), api) 108 | d.sessionID = "12345" 109 | 110 | _, err := d.Cookie("test") 111 | if err == nil || !IsCommunicationError(err) { 112 | t.Errorf(apiCommunicationErrorText) 113 | } 114 | } 115 | 116 | func Test_CookieCookie_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 117 | api := &testableAPIService{ 118 | jsonToReturn: "Invalid JSON!", 119 | errorToReturn: nil, 120 | } 121 | 122 | d := setUpDriver(setUpDefaultCaps(), api) 123 | d.sessionID = "12345" 124 | 125 | _, err := d.Cookie("test") 126 | if err == nil || !IsUnmarshallingError(err) { 127 | t.Errorf(unmarshallingErrorText) 128 | } 129 | } 130 | 131 | func Test_CookieCookie_CorrectResponseIsReturned(t *testing.T) { 132 | api := &testableAPIService{ 133 | jsonToReturn: `{ 134 | "state": "success", 135 | "value": { 136 | "name": "Test Cookie", 137 | "value": "Test Value", 138 | "path": "/", 139 | "domain": "www.google.com", 140 | "secure": true, 141 | "httpOnly": true, 142 | "expiry": "2016-12-25T00:00:00Z" 143 | } 144 | }`, 145 | errorToReturn: nil, 146 | } 147 | 148 | d := setUpDriver(setUpDefaultCaps(), api) 149 | d.sessionID = "12345" 150 | 151 | resp, err := d.Cookie("test") 152 | if err != nil || resp.State != "success" || resp.Cookie.Name != "Test Cookie" || 153 | resp.Cookie.Value != "Test Value" || resp.Cookie.Path != "/" || 154 | resp.Cookie.Domain != "www.google.com" || !resp.Cookie.SecureOnly || 155 | !resp.Cookie.HTTPOnly { 156 | t.Errorf(correctResponseErrorText) 157 | } 158 | } 159 | 160 | /* 161 | AddCookie tests 162 | */ 163 | func Test_CookieAddCookie_InvalidSessionIdResultsInError(t *testing.T) { 164 | api := &testableAPIService{ 165 | jsonToReturn: "", 166 | errorToReturn: nil, 167 | } 168 | 169 | d := setUpDriver(setUpDefaultCaps(), api) 170 | _, err := d.AddCookie(nil) 171 | if err == nil || !IsSessionIDError(err) { 172 | t.Errorf(sessionIDErrorText) 173 | } 174 | } 175 | 176 | func Test_CookieAddCookie_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 177 | api := &testableAPIService{ 178 | jsonToReturn: "", 179 | errorToReturn: errors.New("An error :<"), 180 | } 181 | 182 | d := setUpDriver(setUpDefaultCaps(), api) 183 | d.sessionID = "12345" 184 | 185 | _, err := d.AddCookie(&Cookie{Name: "cookie", Path: "/"}) 186 | if err == nil || !IsCommunicationError(err) { 187 | t.Errorf(apiCommunicationErrorText) 188 | } 189 | } 190 | 191 | func Test_CookieAddCookie_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 192 | api := &testableAPIService{ 193 | jsonToReturn: "Invalid JSON!", 194 | errorToReturn: nil, 195 | } 196 | 197 | d := setUpDriver(setUpDefaultCaps(), api) 198 | d.sessionID = "12345" 199 | 200 | _, err := d.AddCookie(&Cookie{Name: "cookie", Path: "/"}) 201 | if err == nil || !IsUnmarshallingError(err) { 202 | t.Errorf(unmarshallingErrorText) 203 | } 204 | } 205 | 206 | func Test_CookieAddCookie_CorrectResponseIsReturned(t *testing.T) { 207 | api := &testableAPIService{ 208 | jsonToReturn: `{ 209 | "state": "success" 210 | }`, 211 | errorToReturn: nil, 212 | } 213 | 214 | d := setUpDriver(setUpDefaultCaps(), api) 215 | d.sessionID = "12345" 216 | 217 | resp, err := d.AddCookie(&Cookie{Name: "cookie", Path: "/"}) 218 | if err != nil || resp.State != "success" { 219 | t.Errorf(correctResponseErrorText) 220 | } 221 | } 222 | 223 | /* 224 | DeleteCookie tests 225 | */ 226 | func Test_CookieDeleteCookie_InvalidSessionIdResultsInError(t *testing.T) { 227 | api := &testableAPIService{ 228 | jsonToReturn: "", 229 | errorToReturn: nil, 230 | } 231 | 232 | d := setUpDriver(setUpDefaultCaps(), api) 233 | _, err := d.DeleteCookie("") 234 | if err == nil || !IsSessionIDError(err) { 235 | t.Errorf(sessionIDErrorText) 236 | } 237 | } 238 | 239 | func Test_CookieDeleteCookie_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 240 | api := &testableAPIService{ 241 | jsonToReturn: "", 242 | errorToReturn: errors.New("An error :<"), 243 | } 244 | 245 | d := setUpDriver(setUpDefaultCaps(), api) 246 | d.sessionID = "12345" 247 | 248 | _, err := d.DeleteCookie("") 249 | if err == nil || !IsCommunicationError(err) { 250 | t.Errorf(apiCommunicationErrorText) 251 | } 252 | } 253 | 254 | func Test_CookieDeleteCookie_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 255 | api := &testableAPIService{ 256 | jsonToReturn: "Invalid JSON!", 257 | errorToReturn: nil, 258 | } 259 | 260 | d := setUpDriver(setUpDefaultCaps(), api) 261 | d.sessionID = "12345" 262 | 263 | _, err := d.DeleteCookie("") 264 | if err == nil || !IsUnmarshallingError(err) { 265 | t.Errorf(unmarshallingErrorText) 266 | } 267 | } 268 | 269 | func Test_CookieDeleteCookie_CorrectResponseIsReturned(t *testing.T) { 270 | api := &testableAPIService{ 271 | jsonToReturn: `{ 272 | "state": "success" 273 | }`, 274 | errorToReturn: nil, 275 | } 276 | 277 | d := setUpDriver(setUpDefaultCaps(), api) 278 | d.sessionID = "12345" 279 | 280 | resp, err := d.DeleteCookie("") 281 | if err != nil || resp.State != "success" { 282 | t.Errorf(correctResponseErrorText) 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /remote_driver_document.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import "fmt" 4 | 5 | // PageSourceResponse is the response returned from calling the PageSource 6 | // method. 7 | type PageSourceResponse struct { 8 | State string 9 | Source string 10 | } 11 | 12 | // ExecuteScriptResponse is the response returned from calling the ExecuteScript 13 | // method. 14 | type ExecuteScriptResponse struct { 15 | State string 16 | Response string 17 | } 18 | 19 | func (s *seleniumWebDriver) PageSource() (*PageSourceResponse, error) { 20 | if len(s.sessionID) == 0 { 21 | return nil, newSessionIDError("PageSource") 22 | } 23 | 24 | var err error 25 | 26 | url := fmt.Sprintf("%s/session/%s/source", s.seleniumURL, s.sessionID) 27 | 28 | resp, err := s.valueRequest(&request{ 29 | url: url, 30 | method: "GET", 31 | body: nil, 32 | callingMethod: "PageSource", 33 | }) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | return &PageSourceResponse{State: resp.State, Source: resp.Value}, nil 39 | } 40 | 41 | func (s *seleniumWebDriver) ExecuteScript(script string) (*ExecuteScriptResponse, error) { 42 | if len(s.sessionID) == 0 { 43 | return nil, newSessionIDError("ExecuteScript") 44 | } 45 | 46 | url := fmt.Sprintf("%s/session/%s/execute", s.seleniumURL, s.sessionID) 47 | 48 | return s.scriptRequest(script, url, "ExecuteScript") 49 | } 50 | 51 | func (s *seleniumWebDriver) ExecuteScriptAsync(script string) (*ExecuteScriptResponse, error) { 52 | if len(s.sessionID) == 0 { 53 | return nil, newSessionIDError("ExecuteScriptAsync") 54 | } 55 | 56 | url := fmt.Sprintf("%s/session/%s/execute_async", s.seleniumURL, s.sessionID) 57 | 58 | return s.scriptRequest(script, url, "ExecuteScriptAsync") 59 | } 60 | -------------------------------------------------------------------------------- /remote_driver_document_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | /* 9 | PageSource tests 10 | */ 11 | func Test_DocumentPageSource_InvalidSessionIDResultsInError(t *testing.T) { 12 | api := &testableAPIService{ 13 | jsonToReturn: "", 14 | errorToReturn: nil, 15 | } 16 | 17 | d := setUpDriver(setUpDefaultCaps(), api) 18 | 19 | _, err := d.PageSource() 20 | if err == nil || !IsSessionIDError(err) { 21 | t.Errorf(sessionIDErrorText) 22 | } 23 | } 24 | 25 | func Test_DocumentPageSource_CommunicationErrorIsReturned(t *testing.T) { 26 | api := &testableAPIService{ 27 | jsonToReturn: "", 28 | errorToReturn: errors.New("An error"), 29 | } 30 | 31 | d := setUpDriver(setUpDefaultCaps(), api) 32 | d.sessionID = "12345" 33 | 34 | _, err := d.PageSource() 35 | if err == nil || !IsCommunicationError(err) { 36 | t.Errorf(apiCommunicationErrorText) 37 | } 38 | } 39 | 40 | func Test_DocumentPageSource_UnmarshallingErrorIsReturned(t *testing.T) { 41 | api := &testableAPIService{ 42 | jsonToReturn: "Invalid JSON", 43 | errorToReturn: nil, 44 | } 45 | 46 | d := setUpDriver(setUpDefaultCaps(), api) 47 | d.sessionID = "12345" 48 | 49 | _, err := d.PageSource() 50 | if err == nil || !IsUnmarshallingError(err) { 51 | t.Errorf(unmarshallingErrorText) 52 | } 53 | } 54 | 55 | func Test_DocumentPageSource_ResultIsReturnedSuccessfully(t *testing.T) { 56 | api := &testableAPIService{ 57 | jsonToReturn: `{ 58 | "state": "success", 59 | "value": "this would be HTML" 60 | }`, 61 | errorToReturn: nil, 62 | } 63 | 64 | d := setUpDriver(setUpDefaultCaps(), api) 65 | d.sessionID = "12345" 66 | 67 | resp, err := d.PageSource() 68 | if err != nil || resp.State != "success" || resp.Source != "this would be HTML" { 69 | t.Errorf(correctResponseErrorText) 70 | } 71 | } 72 | 73 | /* 74 | ExecuteScript tests 75 | */ 76 | func Test_CommandExecuteScript_InvalidSessionIDResultsInError(t *testing.T) { 77 | api := &testableAPIService{ 78 | jsonToReturn: "", 79 | errorToReturn: nil, 80 | } 81 | 82 | d := setUpDriver(setUpDefaultCaps(), api) 83 | 84 | _, err := d.ExecuteScript("alert('test');") 85 | if err == nil || !IsSessionIDError(err) { 86 | t.Errorf(sessionIDErrorText) 87 | } 88 | } 89 | 90 | func Test_CommandExecuteScript_CommunicationErrorIsReturned(t *testing.T) { 91 | api := &testableAPIService{ 92 | jsonToReturn: "", 93 | errorToReturn: errors.New("An error"), 94 | } 95 | 96 | d := setUpDriver(setUpDefaultCaps(), api) 97 | d.sessionID = "12345" 98 | 99 | _, err := d.ExecuteScript("alert('test');") 100 | if err == nil || !IsCommunicationError(err) { 101 | t.Errorf(apiCommunicationErrorText) 102 | } 103 | } 104 | 105 | func Test_CommandExecuteScript_UnmarshallingErrorIsReturned(t *testing.T) { 106 | api := &testableAPIService{ 107 | jsonToReturn: "Invalid JSON", 108 | errorToReturn: nil, 109 | } 110 | 111 | d := setUpDriver(setUpDefaultCaps(), api) 112 | d.sessionID = "12345" 113 | 114 | _, err := d.ExecuteScript("alert('test');") 115 | if err == nil || !IsUnmarshallingError(err) { 116 | t.Errorf(unmarshallingErrorText) 117 | } 118 | } 119 | 120 | func Test_CommandExecuteScript_ResultIsReturnedSuccessfully(t *testing.T) { 121 | api := &testableAPIService{ 122 | jsonToReturn: `{ 123 | "state": "success", 124 | "value": "test" 125 | }`, 126 | errorToReturn: nil, 127 | } 128 | 129 | d := setUpDriver(setUpDefaultCaps(), api) 130 | d.sessionID = "12345" 131 | 132 | resp, err := d.ExecuteScript("alert('test');") 133 | if err != nil || resp.State != "success" { 134 | t.Errorf(correctResponseErrorText) 135 | } 136 | } 137 | 138 | /* 139 | ExecuteScriptAsync tests 140 | */ 141 | func Test_CommandExecuteScriptAsync_InvalidSessionIDResultsInError(t *testing.T) { 142 | api := &testableAPIService{ 143 | jsonToReturn: "", 144 | errorToReturn: nil, 145 | } 146 | 147 | d := setUpDriver(setUpDefaultCaps(), api) 148 | 149 | _, err := d.ExecuteScriptAsync("alert('test');") 150 | if err == nil || !IsSessionIDError(err) { 151 | t.Errorf(sessionIDErrorText) 152 | } 153 | } 154 | 155 | func Test_CommandExecuteScriptAsync_CommunicationErrorIsReturned(t *testing.T) { 156 | api := &testableAPIService{ 157 | jsonToReturn: "", 158 | errorToReturn: errors.New("An error"), 159 | } 160 | 161 | d := setUpDriver(setUpDefaultCaps(), api) 162 | d.sessionID = "12345" 163 | 164 | _, err := d.ExecuteScriptAsync("alert('test');") 165 | if err == nil || !IsCommunicationError(err) { 166 | t.Errorf(apiCommunicationErrorText) 167 | } 168 | } 169 | 170 | func Test_CommandExecuteScriptAsync_UnmarshallingErrorIsReturned(t *testing.T) { 171 | api := &testableAPIService{ 172 | jsonToReturn: "Invalid JSON", 173 | errorToReturn: nil, 174 | } 175 | 176 | d := setUpDriver(setUpDefaultCaps(), api) 177 | d.sessionID = "12345" 178 | 179 | _, err := d.ExecuteScriptAsync("alert('test');") 180 | if err == nil || !IsUnmarshallingError(err) { 181 | t.Errorf(unmarshallingErrorText) 182 | } 183 | } 184 | 185 | func Test_CommandExecuteScriptAsync_ResultIsReturnedSuccessfully(t *testing.T) { 186 | api := &testableAPIService{ 187 | jsonToReturn: `{ 188 | "state": "success", 189 | "value": "test" 190 | }`, 191 | errorToReturn: nil, 192 | } 193 | 194 | d := setUpDriver(setUpDefaultCaps(), api) 195 | d.sessionID = "12345" 196 | 197 | resp, err := d.ExecuteScriptAsync("alert('test');") 198 | if err != nil || resp.State != "success" { 199 | t.Errorf(correctResponseErrorText) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /remote_driver_element.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | type findElementResponse struct { 10 | E element `json:"value"` 11 | } 12 | 13 | type findElementsResponse struct { 14 | E []element `json:"value"` 15 | } 16 | 17 | type element struct { 18 | ID string `json:"element"` 19 | } 20 | 21 | func (s *seleniumWebDriver) FindElement(by By) (Element, error) { 22 | if by.Type() == "index" { 23 | return nil, errors.New("findelement: invalid by argument") 24 | } 25 | if len(s.sessionID) == 0 { 26 | return nil, newSessionIDError("FindElement") 27 | } 28 | 29 | var response findElementResponse 30 | var err error 31 | 32 | url := fmt.Sprintf("%s/session/%s/element", s.seleniumURL, s.sessionID) 33 | 34 | resp, err := s.elementRequest(&elRequest{ 35 | url: url, 36 | by: by, 37 | method: "POST", 38 | callingMethod: "FindElement", 39 | }) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | err = json.Unmarshal(resp, &response) 45 | if err != nil { 46 | return nil, newUnmarshallingError(err, "FindElement", string(resp)) 47 | } 48 | 49 | el := newSeleniumElement(response.E.ID, s) 50 | return el, nil 51 | } 52 | 53 | func (s *seleniumWebDriver) FindElements(by By) ([]Element, error) { 54 | if by.Type() == "index" { 55 | return nil, errors.New("findelements: invalid by argument") 56 | } 57 | if len(s.sessionID) == 0 { 58 | return nil, newSessionIDError("FindElements") 59 | } 60 | 61 | var response findElementsResponse 62 | var err error 63 | 64 | url := fmt.Sprintf("%s/session/%s/elements", s.seleniumURL, s.sessionID) 65 | 66 | resp, err := s.elementRequest(&elRequest{ 67 | url: url, 68 | by: by, 69 | method: "POST", 70 | callingMethod: "FindElements", 71 | }) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | err = json.Unmarshal(resp, &response) 77 | if err != nil { 78 | return nil, newUnmarshallingError(err, "FindElements", string(resp)) 79 | } 80 | 81 | elements := make([]Element, len(response.E)) 82 | for i := range response.E { 83 | elements[i] = newSeleniumElement(response.E[i].ID, s) 84 | } 85 | 86 | return elements, nil 87 | } 88 | -------------------------------------------------------------------------------- /remote_driver_element_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | /* 9 | FIND ELEMENT TESTS 10 | */ 11 | func Test_ElementFindElement_ByIndexResultsInError(t *testing.T) { 12 | api := &testableAPIService{ 13 | jsonToReturn: "", 14 | errorToReturn: nil, 15 | } 16 | 17 | d := setUpDriver(setUpDefaultCaps(), api) 18 | 19 | _, err := d.FindElement(ByIndex(32)) 20 | if err == nil { 21 | t.Errorf(sessionIDErrorText) 22 | } 23 | } 24 | 25 | func Test_ElementFindElement_InvalidSessionIdResultsInError(t *testing.T) { 26 | api := &testableAPIService{ 27 | jsonToReturn: "", 28 | errorToReturn: nil, 29 | } 30 | 31 | d := setUpDriver(setUpDefaultCaps(), api) 32 | 33 | _, err := d.FindElement(ByCSSSelector("test")) 34 | if err == nil || !IsSessionIDError(err) { 35 | t.Errorf(sessionIDErrorText) 36 | } 37 | } 38 | 39 | func Test_ElementFindElement_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 40 | api := &testableAPIService{ 41 | jsonToReturn: "", 42 | errorToReturn: errors.New("An error :<"), 43 | } 44 | 45 | d := setUpDriver(setUpDefaultCaps(), api) 46 | d.sessionID = "12345" 47 | 48 | _, err := d.FindElement(ByCSSSelector("iframe > ul")) 49 | if err == nil || !IsCommunicationError(err) { 50 | t.Errorf(apiCommunicationErrorText) 51 | } 52 | } 53 | 54 | func Test_ElementFindElement_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 55 | api := &testableAPIService{ 56 | jsonToReturn: "Invalid JSON!", 57 | errorToReturn: nil, 58 | } 59 | 60 | d := setUpDriver(setUpDefaultCaps(), api) 61 | d.sessionID = "12345" 62 | 63 | _, err := d.FindElement(ByCSSSelector("iframe > ul")) 64 | if err == nil || !IsUnmarshallingError(err) { 65 | t.Errorf(unmarshallingErrorText) 66 | } 67 | } 68 | 69 | func Test_ElementFindElement_CorrectResponseIsReturned(t *testing.T) { 70 | api := &testableAPIService{ 71 | jsonToReturn: `{ 72 | "state": "success", 73 | "value": { 74 | "ELEMENT": "0" 75 | } 76 | }`, 77 | errorToReturn: nil, 78 | } 79 | 80 | d := setUpDriver(setUpDefaultCaps(), api) 81 | d.sessionID = "12345" 82 | 83 | resp, err := d.FindElement(ByCSSSelector("iframe > ul")) 84 | if err != nil || resp.ID() != "0" { 85 | t.Errorf(correctResponseErrorText) 86 | } 87 | } 88 | 89 | /* 90 | FIND ELEMENTS TESTS 91 | */ 92 | func Test_ElementFindElements_ByIndexResultsInError(t *testing.T) { 93 | api := &testableAPIService{ 94 | jsonToReturn: "", 95 | errorToReturn: nil, 96 | } 97 | 98 | d := setUpDriver(setUpDefaultCaps(), api) 99 | 100 | _, err := d.FindElements(ByIndex(32)) 101 | if err == nil { 102 | t.Errorf(sessionIDErrorText) 103 | } 104 | } 105 | 106 | func Test_ElementFindElements_InvalidSessionIdResultsInError(t *testing.T) { 107 | api := &testableAPIService{ 108 | jsonToReturn: "", 109 | errorToReturn: nil, 110 | } 111 | 112 | d := setUpDriver(setUpDefaultCaps(), api) 113 | 114 | _, err := d.FindElements(ByCSSSelector("test")) 115 | if err == nil || !IsSessionIDError(err) { 116 | t.Errorf(sessionIDErrorText) 117 | } 118 | } 119 | 120 | func Test_ElementFindElements_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 121 | api := &testableAPIService{ 122 | jsonToReturn: "", 123 | errorToReturn: errors.New("An error :<"), 124 | } 125 | 126 | d := setUpDriver(setUpDefaultCaps(), api) 127 | d.sessionID = "12345" 128 | 129 | _, err := d.FindElements(ByCSSSelector("iframe > ul")) 130 | if err == nil || !IsCommunicationError(err) { 131 | t.Errorf(apiCommunicationErrorText) 132 | } 133 | } 134 | 135 | func Test_ElementFindElements_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 136 | api := &testableAPIService{ 137 | jsonToReturn: "Invalid JSON!", 138 | errorToReturn: nil, 139 | } 140 | 141 | d := setUpDriver(setUpDefaultCaps(), api) 142 | d.sessionID = "12345" 143 | 144 | _, err := d.FindElements(ByCSSSelector("iframe > ul")) 145 | if err == nil || !IsUnmarshallingError(err) { 146 | t.Errorf(unmarshallingErrorText) 147 | } 148 | } 149 | 150 | func Test_ElementFindElements_CorrectResponseIsReturned(t *testing.T) { 151 | api := &testableAPIService{ 152 | jsonToReturn: `{ 153 | "state": "success", 154 | "value": [ 155 | {"ELEMENT": "0"}, 156 | {"ELEMENT": "1"} 157 | ] 158 | }`, 159 | errorToReturn: nil, 160 | } 161 | 162 | d := setUpDriver(setUpDefaultCaps(), api) 163 | d.sessionID = "12345" 164 | 165 | resp, err := d.FindElements(ByCSSSelector("iframe > ul")) 166 | if err != nil || len(resp) <= 1 || resp[1].ID() != "1" { 167 | t.Errorf(correctResponseErrorText) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /remote_driver_helpers.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import "time" 4 | 5 | // Until represents a function that will be continuously repeated until it 6 | // succeeds or a timeout is reached. 7 | type Until func(w WebDriver) bool 8 | 9 | // UntilElementPresent attempts to locate an element on the page. It is 10 | // determined as existing if the state is 'Success' and the error is nil. 11 | func UntilElementPresent(by By) Until { 12 | return func(w WebDriver) bool { 13 | _, err := w.FindElement(by) 14 | return err == nil 15 | } 16 | } 17 | 18 | // UntilURLIs checks whether or not the page's URL has changed. 19 | func UntilURLIs(url string) Until { 20 | return func(w WebDriver) bool { 21 | resp, err := w.CurrentURL() 22 | return err == nil && resp.URL == url 23 | } 24 | } 25 | 26 | func (s *seleniumWebDriver) Wait(u Until, timeout time.Duration, sleep time.Duration) bool { 27 | response := make(chan bool, 1) 28 | quit := make(chan bool, 1) 29 | 30 | go func() { 31 | outer: 32 | for { 33 | select { 34 | case <-quit: 35 | break outer 36 | default: 37 | e := u(s) 38 | if e { 39 | response <- true 40 | break outer 41 | } 42 | } 43 | 44 | time.Sleep(sleep) 45 | } 46 | }() 47 | 48 | select { 49 | case r := <-response: 50 | return r 51 | case <-time.After(timeout): 52 | close(quit) 53 | return false 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /remote_driver_navigation.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | // GoResponse is the response returned from the selenium web driver when calling 11 | // the Go() call. Unfortunately, the W3C specification defines that the response 12 | // should only be whether the call succeeded or not. Should there be any redirects 13 | // they will not be catered for in this response. Should you expect any redirects 14 | // to happen, call the CurrentURL() method. 15 | type GoResponse struct { 16 | State string 17 | } 18 | 19 | // CurrentURLResponse is the response returned from the GET Url call. 20 | type CurrentURLResponse struct { 21 | State string 22 | URL string 23 | } 24 | 25 | // BackResponse is the response returned from the Back call. 26 | type BackResponse struct { 27 | State string 28 | } 29 | 30 | // ForwardResponse is the response returned from the Forward call. 31 | type ForwardResponse struct { 32 | State string 33 | } 34 | 35 | // RefreshResponse is the response returned from the Refresh call. 36 | type RefreshResponse struct { 37 | State string 38 | } 39 | 40 | // TitleResponse is the response returned from the Title call. 41 | type TitleResponse struct { 42 | State string 43 | Title string 44 | } 45 | 46 | func (s *seleniumWebDriver) Go(goURL string) (*GoResponse, error) { 47 | if len(s.sessionID) == 0 { 48 | return nil, newSessionIDError("Go") 49 | } 50 | 51 | var err error 52 | 53 | url := fmt.Sprintf("%s/session/%s/url", s.seleniumURL, s.sessionID) 54 | 55 | invalidURL := goURL == "" 56 | validProtocol := strings.HasPrefix(goURL, "https://") || strings.HasPrefix(goURL, "http://") 57 | if invalidURL || !validProtocol { 58 | return nil, newInvalidURLError(goURL) 59 | } 60 | 61 | params := map[string]string{ 62 | "url": goURL, 63 | } 64 | marshalledJSON, err := json.Marshal(params) 65 | if err != nil { 66 | return nil, newMarshallingError(err, "Go", params) 67 | } 68 | 69 | bodyReader := bytes.NewReader([]byte(marshalledJSON)) 70 | resp, err := s.stateRequest(&request{ 71 | url: url, 72 | method: "POST", 73 | body: bodyReader, 74 | callingMethod: "Go", 75 | }) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | return &GoResponse{State: resp.State}, nil 81 | } 82 | 83 | func (s *seleniumWebDriver) CurrentURL() (*CurrentURLResponse, error) { 84 | if len(s.sessionID) == 0 { 85 | return nil, newSessionIDError("CurrentURL") 86 | } 87 | 88 | var response CurrentURLResponse 89 | var err error 90 | 91 | url := fmt.Sprintf("%s/session/%s/url", s.seleniumURL, s.sessionID) 92 | 93 | resp, err := s.valueRequest(&request{ 94 | url: url, 95 | method: "GET", 96 | body: nil, 97 | callingMethod: "CurrentURL", 98 | }) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | response = CurrentURLResponse{ 104 | State: resp.State, 105 | URL: resp.Value, 106 | } 107 | return &response, nil 108 | } 109 | 110 | func (s *seleniumWebDriver) Back() (*BackResponse, error) { 111 | if len(s.sessionID) == 0 { 112 | return nil, newSessionIDError("Back") 113 | } 114 | 115 | var err error 116 | 117 | url := fmt.Sprintf("%s/session/%s/back", s.seleniumURL, s.sessionID) 118 | 119 | resp, err := s.stateRequest(&request{ 120 | url: url, 121 | method: "POST", 122 | body: nil, 123 | callingMethod: "Back", 124 | }) 125 | if err != nil { 126 | return nil, err 127 | } 128 | 129 | return &BackResponse{State: resp.State}, nil 130 | } 131 | 132 | func (s *seleniumWebDriver) Forward() (*ForwardResponse, error) { 133 | if len(s.sessionID) == 0 { 134 | return nil, newSessionIDError("Forward") 135 | } 136 | 137 | var err error 138 | 139 | url := fmt.Sprintf("%s/session/%s/forward", s.seleniumURL, s.sessionID) 140 | 141 | resp, err := s.stateRequest(&request{ 142 | url: url, 143 | method: "POST", 144 | body: nil, 145 | callingMethod: "Forward", 146 | }) 147 | if err != nil { 148 | return nil, err 149 | } 150 | 151 | return &ForwardResponse{State: resp.State}, nil 152 | } 153 | 154 | func (s *seleniumWebDriver) Refresh() (*RefreshResponse, error) { 155 | if len(s.sessionID) == 0 { 156 | return nil, newSessionIDError("Refresh") 157 | } 158 | 159 | var err error 160 | 161 | url := fmt.Sprintf("%s/session/%s/refresh", s.seleniumURL, s.sessionID) 162 | 163 | resp, err := s.stateRequest(&request{ 164 | url: url, 165 | method: "POST", 166 | body: nil, 167 | callingMethod: "Refresh", 168 | }) 169 | if err != nil { 170 | return nil, err 171 | } 172 | 173 | return &RefreshResponse{State: resp.State}, nil 174 | } 175 | 176 | func (s *seleniumWebDriver) Title() (*TitleResponse, error) { 177 | if len(s.sessionID) == 0 { 178 | return nil, newSessionIDError("Title") 179 | } 180 | 181 | var response TitleResponse 182 | var err error 183 | 184 | url := fmt.Sprintf("%s/session/%s/title", s.seleniumURL, s.sessionID) 185 | 186 | resp, err := s.valueRequest(&request{ 187 | url: url, 188 | method: "GET", 189 | body: nil, 190 | callingMethod: "Title", 191 | }) 192 | if err != nil { 193 | return nil, err 194 | } 195 | 196 | response = TitleResponse{ 197 | State: resp.State, 198 | Title: resp.Value, 199 | } 200 | return &response, nil 201 | } 202 | -------------------------------------------------------------------------------- /remote_driver_navigation_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | /* 9 | Navigation Go Tests 10 | */ 11 | func Test_NavigateGo_NoSessionIdCausesError(t *testing.T) { 12 | api := &testableAPIService{ 13 | jsonToReturn: "", 14 | errorToReturn: nil, 15 | } 16 | 17 | d := setUpDriver(setUpDefaultCaps(), api) 18 | _, err := d.Go("http://google.com") 19 | if err == nil || !IsSessionIDError(err) { 20 | t.Errorf(sessionIDErrorText) 21 | } 22 | } 23 | 24 | func Test_NavigateGo_InvalidURLFormatResultsInError(t *testing.T) { 25 | invalidURLs := []string{ 26 | "", 27 | "google.com", 28 | "htt://google.com", 29 | "://google.com", 30 | "/\\", 31 | } 32 | 33 | for _, i := range invalidURLs { 34 | api := &testableAPIService{ 35 | jsonToReturn: "", 36 | errorToReturn: nil, 37 | } 38 | 39 | d := setUpDriver(setUpDefaultCaps(), api) 40 | d.sessionID = "12345" 41 | 42 | _, err := d.Go(i) 43 | if err == nil || !IsInvalidURLError(err) { 44 | t.Errorf("URL error was not returned") 45 | } 46 | } 47 | } 48 | 49 | func Test_NavigateGo_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 50 | api := &testableAPIService{ 51 | jsonToReturn: "", 52 | errorToReturn: errors.New("An error! :<"), 53 | } 54 | 55 | d := setUpDriver(setUpDefaultCaps(), api) 56 | d.sessionID = "12345" 57 | 58 | _, err := d.Go("https://www.google.com") 59 | if err == nil || !IsCommunicationError(err) { 60 | t.Errorf(apiCommunicationErrorText) 61 | } 62 | } 63 | 64 | func Test_NavigateGo_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 65 | api := &testableAPIService{ 66 | jsonToReturn: "Invalid JSON", 67 | errorToReturn: nil, 68 | } 69 | 70 | d := setUpDriver(setUpDefaultCaps(), api) 71 | d.sessionID = "12345" 72 | 73 | _, err := d.Go("https://www.google.com") 74 | if err == nil || !IsUnmarshallingError(err) { 75 | t.Errorf(unmarshallingErrorText) 76 | } 77 | } 78 | 79 | func Test_NavigateGo_ResultIsUnmarshalledSuccessfully(t *testing.T) { 80 | api := &testableAPIService{ 81 | jsonToReturn: `{ 82 | "state": "success" 83 | }`, 84 | errorToReturn: nil, 85 | } 86 | 87 | d := setUpDriver(setUpDefaultCaps(), api) 88 | d.sessionID = "12345" 89 | 90 | resp, err := d.Go("https://www.google.com") 91 | if err != nil || resp.State != "success" { 92 | t.Errorf(correctResponseErrorText) 93 | } 94 | } 95 | 96 | /* 97 | CurrentURL tests 98 | */ 99 | func Test_NavigateCurrentURL_InvalidSessionIdResultsInError(t *testing.T) { 100 | api := &testableAPIService{ 101 | jsonToReturn: "", 102 | errorToReturn: nil, 103 | } 104 | 105 | d := setUpDriver(setUpDefaultCaps(), api) 106 | _, err := d.CurrentURL() 107 | if err == nil || !IsSessionIDError(err) { 108 | t.Errorf(sessionIDErrorText) 109 | } 110 | } 111 | 112 | func Test_NavigateCurrentURL_CommunicationFailureResultsInError(t *testing.T) { 113 | api := &testableAPIService{ 114 | jsonToReturn: "", 115 | errorToReturn: errors.New("AN error :< "), 116 | } 117 | 118 | d := setUpDriver(setUpDefaultCaps(), api) 119 | d.sessionID = "12345" 120 | _, err := d.CurrentURL() 121 | if err == nil || !IsCommunicationError(err) { 122 | t.Errorf(apiCommunicationErrorText) 123 | } 124 | } 125 | 126 | func Test_NavigateCurrentURL_UnmarshallingFailureResultsInError(t *testing.T) { 127 | api := &testableAPIService{ 128 | jsonToReturn: "Invalid JSON", 129 | errorToReturn: nil, 130 | } 131 | 132 | d := setUpDriver(setUpDefaultCaps(), api) 133 | d.sessionID = "12345" 134 | _, err := d.CurrentURL() 135 | if err == nil || !IsUnmarshallingError(err) { 136 | t.Errorf(unmarshallingErrorText) 137 | } 138 | } 139 | 140 | func Test_NavigateCurrentURL_SuccessfulResultGetsReturnedCorrectly(t *testing.T) { 141 | api := &testableAPIService{ 142 | jsonToReturn: `{ 143 | "state": "success", 144 | "value": "http://google.com" 145 | }`, 146 | errorToReturn: nil, 147 | } 148 | 149 | d := setUpDriver(setUpDefaultCaps(), api) 150 | d.sessionID = "12345" 151 | resp, err := d.CurrentURL() 152 | if err != nil || resp.URL != "http://google.com" || resp.State != "success" { 153 | t.Errorf(correctResponseErrorText) 154 | } 155 | } 156 | 157 | /* 158 | Back tests 159 | */ 160 | func Test_NavigateBack_InvalidSessionIdResultsInAnError(t *testing.T) { 161 | api := &testableAPIService{ 162 | jsonToReturn: "", 163 | errorToReturn: nil, 164 | } 165 | 166 | d := setUpDriver(setUpDefaultCaps(), api) 167 | _, err := d.Back() 168 | if err == nil || !IsSessionIDError(err) { 169 | t.Errorf(sessionIDErrorText) 170 | } 171 | } 172 | 173 | func Test_NavigateBack_CommunicationFailureResultsInError(t *testing.T) { 174 | api := &testableAPIService{ 175 | jsonToReturn: "", 176 | errorToReturn: errors.New("AN error :< "), 177 | } 178 | 179 | d := setUpDriver(setUpDefaultCaps(), api) 180 | d.sessionID = "12345" 181 | _, err := d.Back() 182 | if err == nil || !IsCommunicationError(err) { 183 | t.Errorf(apiCommunicationErrorText) 184 | } 185 | } 186 | 187 | func Test_NavigateBack__UnmarshallingFailureResultsInAnError(t *testing.T) { 188 | api := &testableAPIService{ 189 | jsonToReturn: "Invalid JSON", 190 | errorToReturn: nil, 191 | } 192 | 193 | d := setUpDriver(setUpDefaultCaps(), api) 194 | d.sessionID = "12345" 195 | _, err := d.Back() 196 | if err == nil || !IsUnmarshallingError(err) { 197 | t.Errorf(unmarshallingErrorText) 198 | } 199 | } 200 | 201 | func Test_NavigateBack_SuccessfulResultGetsReturnedCorrectly(t *testing.T) { 202 | api := &testableAPIService{ 203 | jsonToReturn: `{ 204 | "state": "success" 205 | }`, 206 | errorToReturn: nil, 207 | } 208 | 209 | d := setUpDriver(setUpDefaultCaps(), api) 210 | d.sessionID = "12345" 211 | resp, err := d.Back() 212 | if err != nil || resp.State != "success" { 213 | t.Errorf(correctResponseErrorText) 214 | } 215 | } 216 | 217 | /* 218 | Forward tests 219 | */ 220 | func Test_NavigateForward_InvalidSessionIdResultsInAnError(t *testing.T) { 221 | api := &testableAPIService{ 222 | jsonToReturn: "", 223 | errorToReturn: nil, 224 | } 225 | 226 | d := setUpDriver(setUpDefaultCaps(), api) 227 | _, err := d.Forward() 228 | if err == nil || !IsSessionIDError(err) { 229 | t.Errorf(sessionIDErrorText) 230 | } 231 | } 232 | 233 | func Test_NavigateForward_CommunicationFailureResultsInError(t *testing.T) { 234 | api := &testableAPIService{ 235 | jsonToReturn: "", 236 | errorToReturn: errors.New("AN error :< "), 237 | } 238 | 239 | d := setUpDriver(setUpDefaultCaps(), api) 240 | d.sessionID = "12345" 241 | _, err := d.Forward() 242 | if err == nil || !IsCommunicationError(err) { 243 | t.Errorf(apiCommunicationErrorText) 244 | } 245 | } 246 | 247 | func Test_NavigateForward__UnmarshallingFailureResultsInAnError(t *testing.T) { 248 | api := &testableAPIService{ 249 | jsonToReturn: "Invalid JSON", 250 | errorToReturn: nil, 251 | } 252 | 253 | d := setUpDriver(setUpDefaultCaps(), api) 254 | d.sessionID = "12345" 255 | _, err := d.Forward() 256 | if err == nil || !IsUnmarshallingError(err) { 257 | t.Errorf(unmarshallingErrorText) 258 | } 259 | } 260 | 261 | func Test_NavigateForward_SuccessfulResultGetsReturnedCorrectly(t *testing.T) { 262 | api := &testableAPIService{ 263 | jsonToReturn: `{ 264 | "state": "success" 265 | }`, 266 | errorToReturn: nil, 267 | } 268 | 269 | d := setUpDriver(setUpDefaultCaps(), api) 270 | d.sessionID = "12345" 271 | resp, err := d.Forward() 272 | if err != nil || resp.State != "success" { 273 | t.Errorf(correctResponseErrorText) 274 | } 275 | } 276 | 277 | /* 278 | Refresh tests 279 | */ 280 | func Test_NavigateRefresh_InvalidSessionIdResultsInAnError(t *testing.T) { 281 | api := &testableAPIService{ 282 | jsonToReturn: "", 283 | errorToReturn: nil, 284 | } 285 | 286 | d := setUpDriver(setUpDefaultCaps(), api) 287 | _, err := d.Refresh() 288 | if err == nil || !IsSessionIDError(err) { 289 | t.Errorf(sessionIDErrorText) 290 | } 291 | } 292 | 293 | func Test_NavigateRefresh_CommunicationFailureResultsInError(t *testing.T) { 294 | api := &testableAPIService{ 295 | jsonToReturn: "", 296 | errorToReturn: errors.New("AN error :< "), 297 | } 298 | 299 | d := setUpDriver(setUpDefaultCaps(), api) 300 | d.sessionID = "12345" 301 | _, err := d.Refresh() 302 | if err == nil || !IsCommunicationError(err) { 303 | t.Errorf(apiCommunicationErrorText) 304 | } 305 | } 306 | 307 | func Test_NavigateRefresh_UnmarshallingFailureResultsInError(t *testing.T) { 308 | api := &testableAPIService{ 309 | jsonToReturn: "Invalid JSON", 310 | errorToReturn: nil, 311 | } 312 | 313 | d := setUpDriver(setUpDefaultCaps(), api) 314 | d.sessionID = "12345" 315 | _, err := d.Refresh() 316 | if err == nil || !IsUnmarshallingError(err) { 317 | t.Errorf(unmarshallingErrorText) 318 | } 319 | } 320 | 321 | func Test_NavigateRefresh_SuccessfulResultGetsReturnedCorrectly(t *testing.T) { 322 | api := &testableAPIService{ 323 | jsonToReturn: `{ 324 | "state": "success" 325 | }`, 326 | errorToReturn: nil, 327 | } 328 | 329 | d := setUpDriver(setUpDefaultCaps(), api) 330 | d.sessionID = "12345" 331 | resp, err := d.Refresh() 332 | if err != nil || resp.State != "success" { 333 | t.Errorf(correctResponseErrorText) 334 | } 335 | } 336 | 337 | /* 338 | Title tests 339 | */ 340 | func Test_NavigateTitle_InvalidSessionIdResultsInAnError(t *testing.T) { 341 | api := &testableAPIService{ 342 | jsonToReturn: "", 343 | errorToReturn: nil, 344 | } 345 | 346 | d := setUpDriver(setUpDefaultCaps(), api) 347 | _, err := d.Title() 348 | if err == nil || !IsSessionIDError(err) { 349 | t.Errorf(sessionIDErrorText) 350 | } 351 | } 352 | 353 | func Test_NavigateTitle_CommunicationFailureResultsInError(t *testing.T) { 354 | api := &testableAPIService{ 355 | jsonToReturn: "", 356 | errorToReturn: errors.New("AN error :< "), 357 | } 358 | 359 | d := setUpDriver(setUpDefaultCaps(), api) 360 | d.sessionID = "12345" 361 | _, err := d.Title() 362 | if err == nil || !IsCommunicationError(err) { 363 | t.Errorf(apiCommunicationErrorText) 364 | } 365 | } 366 | 367 | func Test_NavigateTitle_UnmarshallingFailureResultsInError(t *testing.T) { 368 | api := &testableAPIService{ 369 | jsonToReturn: "Invalid JSON", 370 | errorToReturn: nil, 371 | } 372 | 373 | d := setUpDriver(setUpDefaultCaps(), api) 374 | d.sessionID = "12345" 375 | _, err := d.Title() 376 | if err == nil || !IsUnmarshallingError(err) { 377 | t.Errorf(unmarshallingErrorText) 378 | } 379 | } 380 | 381 | func Test_NavigateTitle_SuccessfulResultGetsReturnedCorrectly(t *testing.T) { 382 | api := &testableAPIService{ 383 | jsonToReturn: `{ 384 | "state": "success", 385 | "value": "Google" 386 | }`, 387 | errorToReturn: nil, 388 | } 389 | 390 | d := setUpDriver(setUpDefaultCaps(), api) 391 | d.sessionID = "12345" 392 | resp, err := d.Title() 393 | if err != nil || resp.State != "success" || resp.Title != "Google" { 394 | t.Errorf(correctResponseErrorText) 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /remote_driver_screenshot.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | ) 7 | 8 | // ScreenshotResponse is the response returned from the Screenshot and 9 | // ScreenshotElement methods. 10 | type ScreenshotResponse struct { 11 | State string 12 | EncodedImage string 13 | } 14 | 15 | // ImageBytes is a helpful function for decoding the base64 encoded image URL. 16 | // The image returned is a PNG image and as such can be manipulated by the 17 | // image/png package. Trying to save this as any other image type will 18 | // result in it failing to open. 19 | func (s *ScreenshotResponse) ImageBytes() ([]byte, error) { 20 | return base64.StdEncoding.DecodeString(s.EncodedImage) 21 | } 22 | 23 | func (s *seleniumWebDriver) Screenshot() (*ScreenshotResponse, error) { 24 | if len(s.sessionID) == 0 { 25 | return nil, newSessionIDError("Screenshot") 26 | } 27 | 28 | var err error 29 | 30 | url := fmt.Sprintf("%s/session/%s/screenshot", s.seleniumURL, s.sessionID) 31 | 32 | resp, err := s.valueRequest(&request{ 33 | url: url, 34 | method: "GET", 35 | body: nil, 36 | callingMethod: "Screenshot", 37 | }) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return &ScreenshotResponse{State: resp.State, EncodedImage: resp.Value}, nil 43 | } 44 | -------------------------------------------------------------------------------- /remote_driver_screenshot_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func Test_ScreenshotScreenshot_NoSessionIdCausesError(t *testing.T) { 9 | api := &testableAPIService{ 10 | jsonToReturn: "", 11 | errorToReturn: nil, 12 | } 13 | 14 | d := setUpDriver(setUpDefaultCaps(), api) 15 | 16 | _, err := d.Screenshot() 17 | if err == nil || !IsSessionIDError(err) { 18 | t.Errorf(sessionIDErrorText) 19 | } 20 | } 21 | 22 | func Test_ScreenshotScreenshot_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 23 | api := &testableAPIService{ 24 | jsonToReturn: "", 25 | errorToReturn: errors.New("An error! :<"), 26 | } 27 | 28 | d := setUpDriver(setUpDefaultCaps(), api) 29 | d.sessionID = "12345" 30 | 31 | _, err := d.Screenshot() 32 | if err == nil || !IsCommunicationError(err) { 33 | t.Errorf(apiCommunicationErrorText) 34 | } 35 | } 36 | 37 | func Test_ScreenshotScreenshot_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 38 | api := &testableAPIService{ 39 | jsonToReturn: "Invalid JSON", 40 | errorToReturn: nil, 41 | } 42 | 43 | d := setUpDriver(setUpDefaultCaps(), api) 44 | d.sessionID = "12345" 45 | 46 | _, err := d.Screenshot() 47 | if err == nil || !IsUnmarshallingError(err) { 48 | t.Errorf(unmarshallingErrorText) 49 | } 50 | } 51 | 52 | func Test_ScreenshotScreen_CorrectResponseCanBeReturned(t *testing.T) { 53 | api := &testableAPIService{ 54 | jsonToReturn: `{ 55 | "state": "success", 56 | "value": "dGVzdA==" 57 | }`, 58 | errorToReturn: nil, 59 | } 60 | 61 | d := setUpDriver(setUpDefaultCaps(), api) 62 | d.sessionID = "12345" 63 | 64 | resp, err := d.Screenshot() 65 | if err != nil || resp.State != "success" || resp.EncodedImage != "dGVzdA==" { 66 | t.Errorf(correctResponseErrorText) 67 | } 68 | } 69 | 70 | func Test_ScreenshotScreenshot_Base64StringCanBeDecoded(t *testing.T) { 71 | api := &testableAPIService{ 72 | jsonToReturn: `{ 73 | "state": "success", 74 | "value": "dGVzdA==" 75 | }`, 76 | errorToReturn: nil, 77 | } 78 | 79 | d := setUpDriver(setUpDefaultCaps(), api) 80 | d.sessionID = "12345" 81 | 82 | resp, err := d.Screenshot() 83 | if err != nil || resp.State != "success" { 84 | t.Errorf(correctResponseErrorText) 85 | } 86 | 87 | bytes, err := resp.ImageBytes() 88 | if err != nil || len(bytes) == 0 { 89 | t.Errorf(correctResponseErrorText) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /remote_driver_session.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | ) 8 | 9 | // CreateSessionResponse is the response returned from the API when the 10 | // CreateSession() method does not throw an error. 11 | type CreateSessionResponse struct { 12 | Capabilities CreateSessionCapabilities `json:"value"` 13 | SessionID string `json:"sessionId"` 14 | } 15 | 16 | // CreateSessionCapabilities is a summarisation of the capabilities returned 17 | // from the CreateSession method. 18 | type CreateSessionCapabilities struct { 19 | AcceptInsecureCerts bool `json:"acceptSslCerts"` 20 | BrowserName string `json:"browserName"` 21 | BrowserVersion string `json:"browserVersion"` 22 | PlatformName string `json:"platformVersion"` 23 | } 24 | 25 | // DeleteSessionResponse is the response returned from the API when the 26 | // DeleteSession() method does not thrown an error. 27 | type DeleteSessionResponse struct { 28 | State string `json:"state"` 29 | SessionID string `json:"sessionId"` 30 | } 31 | 32 | // SessionStatusResponse is the response returned from the API when the 33 | // SessionStatus() method is called. 34 | type SessionStatusResponse struct { 35 | State string 36 | } 37 | 38 | // SetSessionTimeoutResponse is the response returned from the API when the 39 | // SetSessionTimeoutResponse() method is called. 40 | type SetSessionTimeoutResponse struct { 41 | State string 42 | } 43 | 44 | func (s *seleniumWebDriver) CreateSession() (*CreateSessionResponse, error) { 45 | var response CreateSessionResponse 46 | var err error 47 | 48 | url := fmt.Sprintf("%s/session", s.seleniumURL) 49 | 50 | capabilitiesJSON, err := s.capabilities.toJSON() 51 | if err != nil { 52 | return nil, newMarshallingError(err, "CreateSession", s.capabilities) 53 | } 54 | 55 | body := bytes.NewReader([]byte(capabilitiesJSON)) 56 | resp, err := s.apiService.performRequest(url, "POST", body) 57 | if err != nil { 58 | return nil, newCommunicationError(err, "CreateSession", url, resp) 59 | } 60 | 61 | err = json.Unmarshal(resp, &response) 62 | if err != nil { 63 | return nil, newUnmarshallingError(err, "CreateSession", string(resp)) 64 | } 65 | 66 | s.sessionID = response.SessionID 67 | return &response, nil 68 | } 69 | 70 | func (s *seleniumWebDriver) DeleteSession() (*DeleteSessionResponse, error) { 71 | if len(s.sessionID) == 0 { 72 | return nil, newSessionIDError("DeleteSession") 73 | } 74 | 75 | var response DeleteSessionResponse 76 | var err error 77 | 78 | url := fmt.Sprintf("%s/session/%s", s.seleniumURL, s.sessionID) 79 | 80 | resp, err := s.apiService.performRequest(url, "DELETE", nil) 81 | if err != nil { 82 | return nil, newCommunicationError(err, "DeleteSession", url, resp) 83 | } 84 | 85 | err = json.Unmarshal(resp, &response) 86 | if err != nil { 87 | return nil, newUnmarshallingError(err, "DeleteSession", string(resp)) 88 | } 89 | 90 | return &response, nil 91 | } 92 | 93 | func (s *seleniumWebDriver) SessionStatus() (*SessionStatusResponse, error) { 94 | var err error 95 | 96 | url := fmt.Sprintf("%s/status", s.seleniumURL) 97 | 98 | resp, err := s.stateRequest(&request{ 99 | url: url, 100 | method: "GET", 101 | body: nil, 102 | callingMethod: "SessionStatus", 103 | }) 104 | if err != nil { 105 | return nil, err 106 | } 107 | 108 | return &SessionStatusResponse{State: resp.State}, nil 109 | } 110 | 111 | func (s *seleniumWebDriver) SetSessionTimeout(to Timeout) (*SetSessionTimeoutResponse, error) { 112 | if len(s.sessionID) == 0 { 113 | return nil, newSessionIDError("SetSessionTimeout") 114 | } 115 | 116 | var err error 117 | 118 | url := fmt.Sprintf("%s/session/%s/timeouts", s.seleniumURL, s.sessionID) 119 | 120 | params := map[string]interface{}{ 121 | "type": to.Type(), 122 | "ms": to.Timeout(), 123 | } 124 | marshalledJSON, err := json.Marshal(params) 125 | if err != nil { 126 | return nil, newMarshallingError(err, "SetSessionTimeout", params) 127 | } 128 | 129 | bodyReader := bytes.NewReader([]byte(marshalledJSON)) 130 | resp, err := s.stateRequest(&request{ 131 | url: url, 132 | method: "POST", 133 | body: bodyReader, 134 | callingMethod: "SetSessionTimeout", 135 | }) 136 | if err != nil { 137 | return nil, err 138 | } 139 | 140 | return &SetSessionTimeoutResponse{State: resp.State}, nil 141 | } 142 | -------------------------------------------------------------------------------- /remote_driver_session_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | /* 9 | CREATE SESSION TESTS 10 | */ 11 | func Test_CreateSession_FailedAPIRequestResultsInAnErrorBeingReturned(t *testing.T) { 12 | api := &testableAPIService{ 13 | jsonToReturn: "", 14 | errorToReturn: errors.New("An error :<"), 15 | } 16 | 17 | d := setUpDriver(setUpDefaultCaps(), api) 18 | _, err := d.CreateSession() 19 | if !IsCommunicationError(err) { 20 | t.Errorf(sessionIDErrorText) 21 | } 22 | } 23 | 24 | func Test_CreateSession_ResultGetsUnmarshalledCorrectly(t *testing.T) { 25 | api := &testableAPIService{ 26 | jsonToReturn: `{ 27 | "sessionId": "a45a54d3-5413-425c-84ef-d1190cc0521c" 28 | }`, 29 | errorToReturn: nil, 30 | } 31 | 32 | d := setUpDriver(setUpDefaultCaps(), api) 33 | resp, err := d.CreateSession() 34 | if err != nil || resp.SessionID == "" { 35 | t.Errorf(correctResponseErrorText) 36 | } 37 | } 38 | 39 | func Test_CreateSession_ResultIsAssignedToWebDriver(t *testing.T) { 40 | api := &testableAPIService{ 41 | jsonToReturn: `{ 42 | "sessionId": "a45a54d3-5413-425c-84ef-d1190cc0521c" 43 | }`, 44 | errorToReturn: nil, 45 | } 46 | 47 | d := setUpDriver(setUpDefaultCaps(), api) 48 | _, err := d.CreateSession() 49 | if err != nil || d.sessionID != "a45a54d3-5413-425c-84ef-d1190cc0521c" { 50 | t.Errorf(correctResponseErrorText) 51 | } 52 | } 53 | 54 | func Test_CreateSession_UnmarshallingErrorIsReturned(t *testing.T) { 55 | api := &testableAPIService{ 56 | jsonToReturn: "Invalid JSON", 57 | errorToReturn: nil, 58 | } 59 | 60 | d := setUpDriver(setUpDefaultCaps(), api) 61 | _, err := d.CreateSession() 62 | if err == nil || !IsUnmarshallingError(err) { 63 | t.Errorf(unmarshallingErrorText) 64 | } 65 | } 66 | 67 | /* 68 | DELETE SESSION TESTS 69 | */ 70 | func Test_DeleteSession_WhenSessionIDIsNotSetAnErrorIsThrown(t *testing.T) { 71 | api := &testableAPIService{ 72 | jsonToReturn: "", 73 | errorToReturn: nil, 74 | } 75 | 76 | d := setUpDriver(setUpDefaultCaps(), api) 77 | _, err := d.DeleteSession() 78 | if err == nil || !IsSessionIDError(err) { 79 | t.Errorf(sessionIDErrorText) 80 | } 81 | } 82 | 83 | func Test_DeleteSession_ApiFailureIsHandled(t *testing.T) { 84 | api := &testableAPIService{ 85 | jsonToReturn: "", 86 | errorToReturn: errors.New("This is an error"), 87 | } 88 | 89 | d := setUpDriver(setUpDefaultCaps(), api) 90 | d.sessionID = "12345" 91 | _, err := d.DeleteSession() 92 | if err == nil || !IsCommunicationError(err) { 93 | t.Errorf(apiCommunicationErrorText) 94 | } 95 | } 96 | 97 | func Test_DeleteSession_ResponseIsUnmarshalledCorrectly(t *testing.T) { 98 | api := &testableAPIService{ 99 | jsonToReturn: `{ 100 | "state": "success", 101 | "sessionId": "3cebaef3-4fd0-464f-bd24-0a7170074ad4" 102 | }`, 103 | errorToReturn: nil, 104 | } 105 | 106 | d := setUpDriver(setUpDefaultCaps(), api) 107 | d.sessionID = "12345" 108 | resp, err := d.DeleteSession() 109 | if err != nil || resp.State != "success" { 110 | t.Errorf(correctResponseErrorText) 111 | } 112 | } 113 | 114 | func Test_DeleteSession_UnmarshallingFailureResultsInError(t *testing.T) { 115 | api := &testableAPIService{ 116 | jsonToReturn: "Invalid JSON", 117 | errorToReturn: nil, 118 | } 119 | 120 | d := setUpDriver(setUpDefaultCaps(), api) 121 | d.sessionID = "12345" 122 | _, err := d.DeleteSession() 123 | if err == nil || !IsUnmarshallingError(err) { 124 | t.Errorf(unmarshallingErrorText) 125 | } 126 | } 127 | 128 | /* 129 | Session Status Test 130 | */ 131 | func Test_SessionStatus_ApiFailureIsHandled(t *testing.T) { 132 | api := &testableAPIService{ 133 | jsonToReturn: "", 134 | errorToReturn: errors.New("This is an error"), 135 | } 136 | 137 | d := setUpDriver(setUpDefaultCaps(), api) 138 | _, err := d.SessionStatus() 139 | if err == nil || !IsCommunicationError(err) { 140 | t.Errorf(apiCommunicationErrorText) 141 | } 142 | } 143 | 144 | func Test_SessionStatusResponse_IsUnmarshalledCorrectly(t *testing.T) { 145 | api := &testableAPIService{ 146 | jsonToReturn: `{ 147 | "state": "success" 148 | }`, 149 | errorToReturn: nil, 150 | } 151 | 152 | d := setUpDriver(setUpDefaultCaps(), api) 153 | resp, err := d.SessionStatus() 154 | if err != nil || resp.State != "success" { 155 | t.Errorf(correctResponseErrorText) 156 | } 157 | } 158 | 159 | func Test_SessionStatusResponse_UnmarshallingFailureResultsInError(t *testing.T) { 160 | api := &testableAPIService{ 161 | jsonToReturn: "Invalid JSON", 162 | errorToReturn: nil, 163 | } 164 | 165 | d := setUpDriver(setUpDefaultCaps(), api) 166 | _, err := d.SessionStatus() 167 | if err == nil || !IsUnmarshallingError(err) { 168 | t.Errorf(unmarshallingErrorText) 169 | } 170 | } 171 | 172 | /* 173 | Session Set Timeout Test 174 | */ 175 | func Test_SetSessionTimeout_ErrorIsThrownIfSessionIdNotSet(t *testing.T) { 176 | api := &testableAPIService{ 177 | jsonToReturn: "", 178 | errorToReturn: nil, 179 | } 180 | 181 | d := setUpDriver(setUpDefaultCaps(), api) 182 | _, err := d.SetSessionTimeout(nil) 183 | if err == nil || !IsSessionIDError(err) { 184 | t.Errorf(sessionIDErrorText) 185 | } 186 | } 187 | 188 | func Test_SetSessionTimeout_ApiCommunicationErrorIsHandled(t *testing.T) { 189 | api := &testableAPIService{ 190 | jsonToReturn: "", 191 | errorToReturn: errors.New("An error!"), 192 | } 193 | 194 | d := setUpDriver(setUpDefaultCaps(), api) 195 | d.sessionID = "1" 196 | 197 | var timeouts = []Timeout{ 198 | SessionScriptTimeout(25000), 199 | SessionPageLoadTimeout(25000), 200 | SessionImplicitWaitTimeout(25000), 201 | } 202 | 203 | for _, i := range timeouts { 204 | _, err := d.SetSessionTimeout(i) 205 | if err == nil || !IsCommunicationError(err) { 206 | t.Errorf(apiCommunicationErrorText) 207 | } 208 | } 209 | } 210 | 211 | func Test_SetSessionTimeout_ResponseIsUnmarshalledCorrectly(t *testing.T) { 212 | api := &testableAPIService{ 213 | jsonToReturn: `{ 214 | "state": "success" 215 | }`, 216 | errorToReturn: nil, 217 | } 218 | 219 | d := setUpDriver(setUpDefaultCaps(), api) 220 | d.sessionID = "1" 221 | 222 | var timeouts = []Timeout{ 223 | SessionScriptTimeout(25000), 224 | SessionPageLoadTimeout(25000), 225 | SessionImplicitWaitTimeout(25000), 226 | } 227 | 228 | for _, i := range timeouts { 229 | resp, err := d.SetSessionTimeout(i) 230 | if err != nil || resp.State != "success" { 231 | t.Errorf(correctResponseErrorText) 232 | } 233 | } 234 | } 235 | 236 | func Test_SetSessionTimeout_UnmarshallingFailureResultsInError(t *testing.T) { 237 | api := &testableAPIService{ 238 | jsonToReturn: "Invalid JSON", 239 | errorToReturn: nil, 240 | } 241 | 242 | d := setUpDriver(setUpDefaultCaps(), api) 243 | d.sessionID = "1" 244 | 245 | var timeouts = []Timeout{ 246 | SessionScriptTimeout(25000), 247 | SessionPageLoadTimeout(25000), 248 | SessionImplicitWaitTimeout(25000), 249 | } 250 | 251 | for _, i := range timeouts { 252 | _, err := d.SetSessionTimeout(i) 253 | if err == nil || !IsUnmarshallingError(err) { 254 | t.Errorf(unmarshallingErrorText) 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /remote_driver_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | const ( 10 | apiCommunicationErrorText = "An error was not returned or was not of the API communication type" 11 | sessionIDErrorText = "An error was not returned or was not of the SessionIDError type" 12 | correctResponseErrorText = "An error was returned or the result was not what was expected" 13 | argumentErrorText = "An error was not returned or was not of the ArgumentError type" 14 | unmarshallingErrorText = "An error was not returned or was not of the UnmarshallingError type" 15 | ) 16 | 17 | func setUpDefaultCaps() *Capabilities { 18 | caps := Capabilities{} 19 | caps.SetBrowser(FirefoxBrowser()) 20 | return &caps 21 | } 22 | 23 | func setUpDriver(caps *Capabilities, api apiServicer) *seleniumWebDriver { 24 | return &seleniumWebDriver{ 25 | seleniumURL: "http://localhost:4444/wd/hub/", 26 | capabilities: caps, 27 | apiService: api, 28 | } 29 | } 30 | 31 | type testableAPIService struct { 32 | jsonToReturn string 33 | errorToReturn error 34 | bodyNilError error 35 | } 36 | 37 | func (t *testableAPIService) performRequest(url string, method string, body io.Reader) ([]byte, error) { 38 | json := []byte(t.jsonToReturn) 39 | return json, t.errorToReturn 40 | } 41 | 42 | func Test_NewSelenium_WebDriverCreatesErrorIfSeleniumURLIsInvalid(t *testing.T) { 43 | invalidSeleniumUrls := []string{ 44 | "", 45 | " ", 46 | "myRequirementWithoutProtocol", 47 | } 48 | for _, i := range invalidSeleniumUrls { 49 | caps := setUpDefaultCaps() 50 | _, err := NewSeleniumWebDriver(i, *caps) 51 | if err == nil { 52 | t.Errorf("Passing an invalid remote Selenium URL did not cause an error") 53 | } 54 | } 55 | } 56 | 57 | func Test_NewSelenium_WebDriverCreatesSuccessfullyIfSeleniumURLIsValid(t *testing.T) { 58 | validSeleniumUrls := []string{ 59 | "http://google.com", 60 | "https://google.com", 61 | } 62 | for _, i := range validSeleniumUrls { 63 | caps := setUpDefaultCaps() 64 | w, err := NewSeleniumWebDriver(i, *caps) 65 | if w == nil || err != nil { 66 | t.Errorf("Passing a valid remote Selenium URL caused an error or did not return a valid driver.") 67 | } 68 | } 69 | } 70 | 71 | func Test_NewSelenium_WebDriverCreatesErrorIfCapabilitiesAreEmpty(t *testing.T) { 72 | _, err := NewSeleniumWebDriver("http://google.com", Capabilities{}) 73 | if err == nil { 74 | t.Errorf("Passing an empty capabilities object did not cause an error.") 75 | } 76 | } 77 | 78 | func Test_NewSelenium_TrailingSlashIsRemovedIfTheUserDoesNotSpecifyOne(t *testing.T) { 79 | invalidUrls := []string{ 80 | "http://localhost/", 81 | "http://localhost:444/", 82 | } 83 | for _, i := range invalidUrls { 84 | caps := setUpDefaultCaps() 85 | d, err := NewSeleniumWebDriver(i, *caps) 86 | if err != nil || strings.HasSuffix(d.DriverURL(), "/") { 87 | t.Errorf("Trailing slash was not removed from URL or an error was returned.") 88 | } 89 | } 90 | } 91 | 92 | /* 93 | By tests 94 | */ 95 | func Test_ByByIndex_CorrectIndexReturnsAsExpected(t *testing.T) { 96 | correctIndexes := []uint{ 97 | 1, 98 | 58, 99 | 65535, 100 | } 101 | for _, i := range correctIndexes { 102 | r := ByIndex(i) 103 | if r.Type() != "index" || r.Value().(uint) != i { 104 | t.Errorf(correctResponseErrorText) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /remote_element.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | ) 8 | 9 | func newSeleniumElement(i string, w *seleniumWebDriver) *seleniumElement { 10 | return &seleniumElement{ 11 | id: i, 12 | wd: w, 13 | } 14 | } 15 | 16 | // ElementSelectedResponse is the response returned from the Selected() call. 17 | // The result /should/ always be successfully returned unless there is a 18 | // server error. 19 | type ElementSelectedResponse struct { 20 | State string `json:"state"` 21 | Selected bool `json:"value"` 22 | } 23 | 24 | // ElementAttributeResponse is the response returned from the Attribute call. 25 | type ElementAttributeResponse struct { 26 | State string 27 | Value string 28 | } 29 | 30 | // ElementCSSValueResponse is the response returned when the CSSValue method 31 | // is called on an Element implementation. 32 | type ElementCSSValueResponse struct { 33 | State string 34 | Value string 35 | } 36 | 37 | // ElementTextResponse is the response returned from calling the Text method. 38 | type ElementTextResponse struct { 39 | State string 40 | Text string 41 | } 42 | 43 | // ElementTagNameResponse is the response returned from calling the TagName method. 44 | type ElementTagNameResponse struct { 45 | State string 46 | Tag string 47 | } 48 | 49 | // ElementRectangleResponse is the response returned from calling the Rectangle 50 | // method. 51 | type ElementRectangleResponse struct { 52 | State string 53 | Rectangle Rectangle `json:"value"` 54 | } 55 | 56 | // Rectangle repsents an elements size and position on the page. 57 | type Rectangle struct { 58 | Dimensions 59 | 60 | X int `json:"x"` 61 | Y int `json:"y"` 62 | } 63 | 64 | // ElementEnabledResponse is the response returned from calling the Enabled method. 65 | type ElementEnabledResponse struct { 66 | State string `json:"state"` 67 | Enabled bool `json:"value"` 68 | } 69 | 70 | // ElementClickResponse is the response returned from calling the Click method. 71 | type ElementClickResponse struct { 72 | State string 73 | } 74 | 75 | // ElementClearResponse is the response returned from calling the Clear method. 76 | type ElementClearResponse struct { 77 | State string 78 | } 79 | 80 | // ElementSendKeysResponse is the response returned from calling the SendKeys method. 81 | type ElementSendKeysResponse struct { 82 | State string 83 | } 84 | 85 | type seleniumElement struct { 86 | id string 87 | wd *seleniumWebDriver 88 | } 89 | 90 | func (s *seleniumElement) ID() string { 91 | return s.id 92 | } 93 | 94 | func (s *seleniumElement) Selected() (*ElementSelectedResponse, error) { 95 | var el ElementSelectedResponse 96 | var err error 97 | 98 | url := fmt.Sprintf("%s/session/%s/element/%s/selected", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 99 | 100 | resp, err := s.wd.apiService.performRequest(url, "GET", nil) 101 | if err != nil { 102 | return nil, newCommunicationError(err, "Selected", url, nil) 103 | } 104 | 105 | err = json.Unmarshal(resp, &el) 106 | if err != nil { 107 | return nil, newUnmarshallingError(err, "Selected", string(resp)) 108 | } 109 | 110 | return &el, nil 111 | } 112 | 113 | func (s *seleniumElement) Attribute(att string) (*ElementAttributeResponse, error) { 114 | var err error 115 | 116 | url := fmt.Sprintf("%s/session/%s/element/%s/attribute/%s", s.wd.seleniumURL, s.wd.sessionID, s.ID(), att) 117 | 118 | resp, err := s.wd.valueRequest(&request{ 119 | url: url, 120 | method: "GET", 121 | body: nil, 122 | callingMethod: "Attribute", 123 | }) 124 | if err != nil { 125 | return nil, err 126 | } 127 | 128 | return &ElementAttributeResponse{State: resp.State, Value: resp.Value}, nil 129 | } 130 | 131 | func (s *seleniumElement) CSSValue(prop string) (*ElementCSSValueResponse, error) { 132 | var err error 133 | 134 | url := fmt.Sprintf("%s/session/%s/element/%s/css/%s", s.wd.seleniumURL, s.wd.sessionID, s.ID(), prop) 135 | 136 | resp, err := s.wd.valueRequest(&request{ 137 | url: url, 138 | method: "GET", 139 | body: nil, 140 | callingMethod: "CSSValue", 141 | }) 142 | if err != nil { 143 | return nil, err 144 | } 145 | 146 | return &ElementCSSValueResponse{State: resp.State, Value: resp.Value}, nil 147 | } 148 | 149 | func (s *seleniumElement) Text() (*ElementTextResponse, error) { 150 | var err error 151 | 152 | url := fmt.Sprintf("%s/session/%s/element/%s/text", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 153 | 154 | resp, err := s.wd.valueRequest(&request{ 155 | url: url, 156 | method: "GET", 157 | body: nil, 158 | callingMethod: "Text", 159 | }) 160 | if err != nil { 161 | return nil, err 162 | } 163 | 164 | return &ElementTextResponse{State: resp.State, Text: resp.Value}, nil 165 | } 166 | 167 | func (s *seleniumElement) TagName() (*ElementTagNameResponse, error) { 168 | var err error 169 | 170 | url := fmt.Sprintf("%s/session/%s/element/%s/name", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 171 | 172 | resp, err := s.wd.valueRequest(&request{ 173 | url: url, 174 | method: "GET", 175 | body: nil, 176 | callingMethod: "TagName", 177 | }) 178 | if err != nil { 179 | return nil, err 180 | } 181 | 182 | return &ElementTagNameResponse{State: resp.State, Tag: resp.Value}, nil 183 | } 184 | 185 | func (s *seleniumElement) Rectangle() (*ElementRectangleResponse, error) { 186 | var response ElementRectangleResponse 187 | var err error 188 | 189 | url := fmt.Sprintf("%s/session/%s/element/%s/rect", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 190 | 191 | resp, err := s.wd.apiService.performRequest(url, "GET", nil) 192 | if err != nil { 193 | return nil, newCommunicationError(err, "Rectangle", url, nil) 194 | } 195 | 196 | err = json.Unmarshal(resp, &response) 197 | if err != nil { 198 | return nil, newUnmarshallingError(err, "Rectangle", string(resp)) 199 | } 200 | 201 | return &response, nil 202 | } 203 | 204 | func (s *seleniumElement) Enabled() (*ElementEnabledResponse, error) { 205 | var response ElementEnabledResponse 206 | var err error 207 | 208 | url := fmt.Sprintf("%s/session/%s/element/%s/enabled", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 209 | 210 | resp, err := s.wd.apiService.performRequest(url, "GET", nil) 211 | if err != nil { 212 | return nil, newCommunicationError(err, "Enabled", url, nil) 213 | } 214 | 215 | err = json.Unmarshal(resp, &response) 216 | if err != nil { 217 | return nil, newUnmarshallingError(err, "Enabled", string(resp)) 218 | } 219 | 220 | return &response, nil 221 | } 222 | 223 | func (s *seleniumElement) Click() (*ElementClickResponse, error) { 224 | var err error 225 | 226 | url := fmt.Sprintf("%s/session/%s/element/%s/click", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 227 | 228 | resp, err := s.wd.stateRequest(&request{ 229 | url: url, 230 | method: "POST", 231 | body: nil, 232 | callingMethod: "Click", 233 | }) 234 | if err != nil { 235 | return nil, err 236 | } 237 | 238 | return &ElementClickResponse{State: resp.State}, nil 239 | } 240 | 241 | func (s *seleniumElement) Clear() (*ElementClearResponse, error) { 242 | var err error 243 | 244 | url := fmt.Sprintf("%s/session/%s/element/%s/clear", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 245 | 246 | resp, err := s.wd.stateRequest(&request{ 247 | url: url, 248 | method: "POST", 249 | body: nil, 250 | callingMethod: "Clear", 251 | }) 252 | if err != nil { 253 | return nil, err 254 | } 255 | 256 | return &ElementClearResponse{State: resp.State}, nil 257 | } 258 | 259 | func (s *seleniumElement) SendKeys(keys string) (*ElementSendKeysResponse, error) { 260 | var err error 261 | 262 | url := fmt.Sprintf("%s/session/%s/element/%s/value", s.wd.seleniumURL, s.wd.sessionID, s.ID()) 263 | 264 | keyChars := make([]string, len(keys)) 265 | for i, k := range keys { 266 | keyChars[i] = string(k) 267 | } 268 | dict := map[string][]string{ 269 | "value": keyChars, 270 | } 271 | body, err := json.Marshal(dict) 272 | if err != nil { 273 | return nil, newMarshallingError(err, "SendKeys", dict) 274 | } 275 | 276 | reader := bytes.NewReader(body) 277 | resp, err := s.wd.stateRequest(&request{ 278 | url: url, 279 | method: "POST", 280 | body: reader, 281 | callingMethod: "SendKeys", 282 | }) 283 | if err != nil { 284 | return nil, err 285 | } 286 | 287 | return &ElementSendKeysResponse{State: resp.State}, nil 288 | } 289 | -------------------------------------------------------------------------------- /remote_element_test.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func Test_RemoteElement_IDCanBeRetrieved(t *testing.T) { 9 | el := newSeleniumElement("test", nil) 10 | if el.ID() != "test" { 11 | t.Errorf(correctResponseErrorText) 12 | } 13 | } 14 | 15 | /* SELECTED TESTS 16 | */ 17 | func Test_ElementSelected_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 18 | api := &testableAPIService{ 19 | jsonToReturn: "", 20 | errorToReturn: errors.New("An error :<"), 21 | } 22 | 23 | d := setUpDriver(setUpDefaultCaps(), api) 24 | d.sessionID = "12345" 25 | 26 | el := newSeleniumElement("0", d) 27 | _, err := el.Selected() 28 | if err == nil || !IsCommunicationError(err) { 29 | t.Errorf(apiCommunicationErrorText) 30 | } 31 | } 32 | 33 | func Test_ElementSelected_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 34 | api := &testableAPIService{ 35 | jsonToReturn: "Invalid JSON!", 36 | errorToReturn: nil, 37 | } 38 | 39 | d := setUpDriver(setUpDefaultCaps(), api) 40 | d.sessionID = "12345" 41 | 42 | el := newSeleniumElement("0", d) 43 | _, err := el.Selected() 44 | if err == nil || !IsUnmarshallingError(err) { 45 | t.Errorf(unmarshallingErrorText) 46 | } 47 | } 48 | 49 | func Test_ElementSelected_CorrectResponseIsReturned(t *testing.T) { 50 | api := &testableAPIService{ 51 | jsonToReturn: `{ 52 | "state": "success", 53 | "value": true 54 | }`, 55 | errorToReturn: nil, 56 | } 57 | 58 | d := setUpDriver(setUpDefaultCaps(), api) 59 | d.sessionID = "12345" 60 | 61 | el := newSeleniumElement("0", d) 62 | resp, err := el.Selected() 63 | if err != nil || resp.State != "success" || resp.Selected != true { 64 | t.Errorf(correctResponseErrorText) 65 | } 66 | } 67 | 68 | /* 69 | ATTRIBUTE TESTS 70 | */ 71 | func Test_ElementAttribute_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 72 | api := &testableAPIService{ 73 | jsonToReturn: "", 74 | errorToReturn: errors.New("An error :<"), 75 | } 76 | 77 | d := setUpDriver(setUpDefaultCaps(), api) 78 | d.sessionID = "12345" 79 | 80 | el := newSeleniumElement("0", d) 81 | _, err := el.Attribute("test") 82 | if err == nil || !IsCommunicationError(err) { 83 | t.Errorf(apiCommunicationErrorText) 84 | } 85 | } 86 | 87 | func Test_ElementAttribute_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 88 | api := &testableAPIService{ 89 | jsonToReturn: "Invalid JSON!", 90 | errorToReturn: nil, 91 | } 92 | 93 | d := setUpDriver(setUpDefaultCaps(), api) 94 | d.sessionID = "12345" 95 | 96 | el := newSeleniumElement("0", d) 97 | _, err := el.Attribute("test") 98 | if err == nil || !IsUnmarshallingError(err) { 99 | t.Errorf(unmarshallingErrorText) 100 | } 101 | } 102 | 103 | func Test_ElementAttribute_CorrectResponseIsReturned(t *testing.T) { 104 | api := &testableAPIService{ 105 | jsonToReturn: `{ 106 | "state": "success", 107 | "value": "test value" 108 | }`, 109 | errorToReturn: nil, 110 | } 111 | 112 | d := setUpDriver(setUpDefaultCaps(), api) 113 | d.sessionID = "12345" 114 | 115 | el := newSeleniumElement("0", d) 116 | resp, err := el.Attribute("test") 117 | if err != nil || resp.State != "success" || resp.Value != "test value" { 118 | t.Errorf(correctResponseErrorText) 119 | } 120 | } 121 | 122 | /* 123 | CSSVALUE TESTS 124 | */ 125 | func Test_ElementCSSValue_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 126 | api := &testableAPIService{ 127 | jsonToReturn: "", 128 | errorToReturn: errors.New("An error :<"), 129 | } 130 | 131 | d := setUpDriver(setUpDefaultCaps(), api) 132 | d.sessionID = "12345" 133 | 134 | el := newSeleniumElement("0", d) 135 | _, err := el.CSSValue("test") 136 | if err == nil || !IsCommunicationError(err) { 137 | t.Errorf(apiCommunicationErrorText) 138 | } 139 | } 140 | 141 | func Test_ElementCSSValue_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 142 | api := &testableAPIService{ 143 | jsonToReturn: "Invalid JSON!", 144 | errorToReturn: nil, 145 | } 146 | 147 | d := setUpDriver(setUpDefaultCaps(), api) 148 | d.sessionID = "12345" 149 | 150 | el := newSeleniumElement("0", d) 151 | _, err := el.CSSValue("test") 152 | if err == nil || !IsUnmarshallingError(err) { 153 | t.Errorf(unmarshallingErrorText) 154 | } 155 | } 156 | 157 | func Test_ElementCSSValue_CorrectResponseIsReturned(t *testing.T) { 158 | api := &testableAPIService{ 159 | jsonToReturn: `{ 160 | "state": "success", 161 | "value": "test value" 162 | }`, 163 | errorToReturn: nil, 164 | } 165 | 166 | d := setUpDriver(setUpDefaultCaps(), api) 167 | d.sessionID = "12345" 168 | 169 | el := newSeleniumElement("0", d) 170 | resp, err := el.CSSValue("test") 171 | if err != nil || resp.State != "success" || resp.Value != "test value" { 172 | t.Errorf(correctResponseErrorText) 173 | } 174 | } 175 | 176 | /* 177 | TEXT TESTS 178 | */ 179 | func Test_ElementText_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 180 | api := &testableAPIService{ 181 | jsonToReturn: "", 182 | errorToReturn: errors.New("An error :<"), 183 | } 184 | 185 | d := setUpDriver(setUpDefaultCaps(), api) 186 | d.sessionID = "12345" 187 | 188 | el := newSeleniumElement("0", d) 189 | _, err := el.Text() 190 | if err == nil || !IsCommunicationError(err) { 191 | t.Errorf(apiCommunicationErrorText) 192 | } 193 | } 194 | 195 | func Test_ElementText_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 196 | api := &testableAPIService{ 197 | jsonToReturn: "Invalid JSON!", 198 | errorToReturn: nil, 199 | } 200 | 201 | d := setUpDriver(setUpDefaultCaps(), api) 202 | d.sessionID = "12345" 203 | 204 | el := newSeleniumElement("0", d) 205 | _, err := el.Text() 206 | if err == nil || !IsUnmarshallingError(err) { 207 | t.Errorf(unmarshallingErrorText) 208 | } 209 | } 210 | 211 | func Test_ElementText_CorrectResponseIsReturned(t *testing.T) { 212 | api := &testableAPIService{ 213 | jsonToReturn: `{ 214 | "state": "success", 215 | "value": "test value" 216 | }`, 217 | errorToReturn: nil, 218 | } 219 | 220 | d := setUpDriver(setUpDefaultCaps(), api) 221 | d.sessionID = "12345" 222 | 223 | el := newSeleniumElement("0", d) 224 | resp, err := el.Text() 225 | if err != nil || resp.State != "success" || resp.Text != "test value" { 226 | t.Errorf(correctResponseErrorText) 227 | } 228 | } 229 | 230 | /* 231 | TAG NAME TESTS 232 | */ 233 | func Test_ElementTagName_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 234 | api := &testableAPIService{ 235 | jsonToReturn: "", 236 | errorToReturn: errors.New("An error :<"), 237 | } 238 | 239 | d := setUpDriver(setUpDefaultCaps(), api) 240 | d.sessionID = "12345" 241 | 242 | el := newSeleniumElement("0", d) 243 | _, err := el.TagName() 244 | if err == nil || !IsCommunicationError(err) { 245 | t.Errorf(apiCommunicationErrorText) 246 | } 247 | } 248 | 249 | func Test_ElementTagName_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 250 | api := &testableAPIService{ 251 | jsonToReturn: "Invalid JSON!", 252 | errorToReturn: nil, 253 | } 254 | 255 | d := setUpDriver(setUpDefaultCaps(), api) 256 | d.sessionID = "12345" 257 | 258 | el := newSeleniumElement("0", d) 259 | _, err := el.TagName() 260 | if err == nil || !IsUnmarshallingError(err) { 261 | t.Errorf(unmarshallingErrorText) 262 | } 263 | } 264 | 265 | func Test_ElementTagName_CorrectResponseIsReturned(t *testing.T) { 266 | api := &testableAPIService{ 267 | jsonToReturn: `{ 268 | "state": "success", 269 | "value": "test value" 270 | }`, 271 | errorToReturn: nil, 272 | } 273 | 274 | d := setUpDriver(setUpDefaultCaps(), api) 275 | d.sessionID = "12345" 276 | 277 | el := newSeleniumElement("0", d) 278 | resp, err := el.TagName() 279 | if err != nil || resp.State != "success" || resp.Tag != "test value" { 280 | t.Errorf(correctResponseErrorText) 281 | } 282 | } 283 | 284 | /* 285 | RECTANGLE TESTS 286 | */ 287 | func Test_ElementRectangle_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 288 | api := &testableAPIService{ 289 | jsonToReturn: "", 290 | errorToReturn: errors.New("An error :<"), 291 | } 292 | 293 | d := setUpDriver(setUpDefaultCaps(), api) 294 | d.sessionID = "12345" 295 | 296 | el := newSeleniumElement("0", d) 297 | _, err := el.Rectangle() 298 | if err == nil || !IsCommunicationError(err) { 299 | t.Errorf(apiCommunicationErrorText) 300 | } 301 | } 302 | 303 | func Test_ElementRectangle_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 304 | api := &testableAPIService{ 305 | jsonToReturn: "Invalid JSON!", 306 | errorToReturn: nil, 307 | } 308 | 309 | d := setUpDriver(setUpDefaultCaps(), api) 310 | d.sessionID = "12345" 311 | 312 | el := newSeleniumElement("0", d) 313 | _, err := el.Rectangle() 314 | if err == nil || !IsUnmarshallingError(err) { 315 | t.Errorf(unmarshallingErrorText) 316 | } 317 | } 318 | 319 | func Test_ElementRectangle_CorrectResponseIsReturned(t *testing.T) { 320 | api := &testableAPIService{ 321 | jsonToReturn: `{ 322 | "state": "success", 323 | "value": { 324 | "x": 100, 325 | "y": 200, 326 | "width": 50, 327 | "height": 50 328 | } 329 | }`, 330 | errorToReturn: nil, 331 | } 332 | 333 | d := setUpDriver(setUpDefaultCaps(), api) 334 | d.sessionID = "12345" 335 | 336 | el := newSeleniumElement("0", d) 337 | resp, err := el.Rectangle() 338 | if err != nil || resp.State != "success" || resp.Rectangle.X != 100 || resp.Rectangle.Height != 50 { 339 | t.Errorf(correctResponseErrorText) 340 | } 341 | } 342 | 343 | /* 344 | ENABLED TESTS 345 | */ 346 | func Test_ElementEnabled_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 347 | api := &testableAPIService{ 348 | jsonToReturn: "", 349 | errorToReturn: errors.New("An error :<"), 350 | } 351 | 352 | d := setUpDriver(setUpDefaultCaps(), api) 353 | d.sessionID = "12345" 354 | 355 | el := newSeleniumElement("0", d) 356 | _, err := el.Enabled() 357 | if err == nil || !IsCommunicationError(err) { 358 | t.Errorf(apiCommunicationErrorText) 359 | } 360 | } 361 | 362 | func Test_ElementEnabled_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 363 | api := &testableAPIService{ 364 | jsonToReturn: "Invalid JSON!", 365 | errorToReturn: nil, 366 | } 367 | 368 | d := setUpDriver(setUpDefaultCaps(), api) 369 | d.sessionID = "12345" 370 | 371 | el := newSeleniumElement("0", d) 372 | _, err := el.Enabled() 373 | if err == nil || !IsUnmarshallingError(err) { 374 | t.Errorf(unmarshallingErrorText) 375 | } 376 | } 377 | 378 | func Test_ElementEnabled_CorrectResponseIsReturned(t *testing.T) { 379 | api := &testableAPIService{ 380 | jsonToReturn: `{ 381 | "state": "success", 382 | "value": true 383 | }`, 384 | errorToReturn: nil, 385 | } 386 | 387 | d := setUpDriver(setUpDefaultCaps(), api) 388 | d.sessionID = "12345" 389 | 390 | el := newSeleniumElement("0", d) 391 | resp, err := el.Enabled() 392 | if err != nil || resp.State != "success" || !resp.Enabled { 393 | t.Errorf(correctResponseErrorText) 394 | } 395 | } 396 | 397 | /* 398 | CLICK TESTS 399 | */ 400 | func Test_ElementClick_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 401 | api := &testableAPIService{ 402 | jsonToReturn: "", 403 | errorToReturn: errors.New("An error :<"), 404 | } 405 | 406 | d := setUpDriver(setUpDefaultCaps(), api) 407 | d.sessionID = "12345" 408 | 409 | el := newSeleniumElement("0", d) 410 | _, err := el.Click() 411 | if err == nil || !IsCommunicationError(err) { 412 | t.Errorf(apiCommunicationErrorText) 413 | } 414 | } 415 | 416 | func Test_ElementClick_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 417 | api := &testableAPIService{ 418 | jsonToReturn: "Invalid JSON!", 419 | errorToReturn: nil, 420 | } 421 | 422 | d := setUpDriver(setUpDefaultCaps(), api) 423 | d.sessionID = "12345" 424 | 425 | el := newSeleniumElement("0", d) 426 | _, err := el.Click() 427 | if err == nil || !IsUnmarshallingError(err) { 428 | t.Errorf(unmarshallingErrorText) 429 | } 430 | } 431 | 432 | func Test_ElementClick_CorrectResponseIsReturned(t *testing.T) { 433 | api := &testableAPIService{ 434 | jsonToReturn: `{ 435 | "state": "success" 436 | }`, 437 | errorToReturn: nil, 438 | } 439 | 440 | d := setUpDriver(setUpDefaultCaps(), api) 441 | d.sessionID = "12345" 442 | 443 | el := newSeleniumElement("0", d) 444 | resp, err := el.Click() 445 | if err != nil || resp.State != "success" { 446 | t.Errorf(correctResponseErrorText) 447 | } 448 | } 449 | 450 | /* 451 | CLEAR TESTS 452 | */ 453 | func Test_ElementClear_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 454 | api := &testableAPIService{ 455 | jsonToReturn: "", 456 | errorToReturn: errors.New("An error :<"), 457 | } 458 | 459 | d := setUpDriver(setUpDefaultCaps(), api) 460 | d.sessionID = "12345" 461 | 462 | el := newSeleniumElement("0", d) 463 | _, err := el.Clear() 464 | if err == nil || !IsCommunicationError(err) { 465 | t.Errorf(apiCommunicationErrorText) 466 | } 467 | } 468 | 469 | func Test_ElementClear_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 470 | api := &testableAPIService{ 471 | jsonToReturn: "Invalid JSON!", 472 | errorToReturn: nil, 473 | } 474 | 475 | d := setUpDriver(setUpDefaultCaps(), api) 476 | d.sessionID = "12345" 477 | 478 | el := newSeleniumElement("0", d) 479 | _, err := el.Clear() 480 | if err == nil || !IsUnmarshallingError(err) { 481 | t.Errorf(unmarshallingErrorText) 482 | } 483 | } 484 | 485 | func Test_ElementClear_CorrectResponseIsReturned(t *testing.T) { 486 | api := &testableAPIService{ 487 | jsonToReturn: `{ 488 | "state": "success" 489 | }`, 490 | errorToReturn: nil, 491 | } 492 | 493 | d := setUpDriver(setUpDefaultCaps(), api) 494 | d.sessionID = "12345" 495 | 496 | el := newSeleniumElement("0", d) 497 | resp, err := el.Clear() 498 | if err != nil || resp.State != "success" { 499 | t.Errorf(correctResponseErrorText) 500 | } 501 | } 502 | 503 | /* 504 | SEND KEYS TESTS 505 | */ 506 | func Test_ElementSendKeys_CommunicationErrorIsReturnedCorrectly(t *testing.T) { 507 | api := &testableAPIService{ 508 | jsonToReturn: "", 509 | errorToReturn: errors.New("An error :<"), 510 | } 511 | 512 | d := setUpDriver(setUpDefaultCaps(), api) 513 | d.sessionID = "12345" 514 | 515 | el := newSeleniumElement("0", d) 516 | _, err := el.SendKeys("test") 517 | if err == nil || !IsCommunicationError(err) { 518 | t.Errorf(apiCommunicationErrorText) 519 | } 520 | } 521 | 522 | func Test_ElementSendKeys_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) { 523 | api := &testableAPIService{ 524 | jsonToReturn: "Invalid JSON!", 525 | errorToReturn: nil, 526 | } 527 | 528 | d := setUpDriver(setUpDefaultCaps(), api) 529 | d.sessionID = "12345" 530 | 531 | el := newSeleniumElement("0", d) 532 | _, err := el.SendKeys("test") 533 | if err == nil || !IsUnmarshallingError(err) { 534 | t.Errorf(unmarshallingErrorText) 535 | } 536 | } 537 | 538 | func Test_ElementSendKeys_CorrectResponseIsReturned(t *testing.T) { 539 | api := &testableAPIService{ 540 | jsonToReturn: `{ 541 | "state": "success" 542 | }`, 543 | errorToReturn: nil, 544 | } 545 | 546 | d := setUpDriver(setUpDefaultCaps(), api) 547 | d.sessionID = "12345" 548 | 549 | el := newSeleniumElement("0", d) 550 | resp, err := el.SendKeys("test") 551 | if err != nil || resp.State != "success" { 552 | t.Errorf(correctResponseErrorText) 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /test/integration_tests/alert_acceptalert_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_AlertAcceptAlert_CanAcceptAnAlertCorrectly(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Create session error", err) 13 | } 14 | 15 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/alert.html") 16 | if err != nil { 17 | errorAndWrap(t, "Error navigating to URL", err) 18 | } 19 | 20 | resp, err := driver.AcceptAlert() 21 | if err != nil || resp.State != "success" { 22 | errorAndWrap(t, "Error accepting alert", err) 23 | } 24 | 25 | printObjectResult(resp) 26 | } 27 | -------------------------------------------------------------------------------- /test/integration_tests/alert_alerttext_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_AlertAlertText_CanGetTheAlertText(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error creating session.", err) 13 | } 14 | 15 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/alert.html") 16 | if err != nil { 17 | errorAndWrap(t, "Error visiting URL.", err) 18 | } 19 | 20 | resp, err := driver.AlertText() 21 | if err != nil || resp.State != "success" || resp.Text != "this is an alert" { 22 | errorAndWrap(t, "Error getting alert text.", err) 23 | } 24 | 25 | printObjectResult(resp) 26 | } 27 | -------------------------------------------------------------------------------- /test/integration_tests/alert_dismissalert_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | goselenium "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_AlertDismissAlert_CanDismissAnAlertCorrectly(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error creating session", err) 17 | } 18 | 19 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/alert.html") 20 | if err != nil { 21 | errorAndWrap(t, "Error visiting URL", err) 22 | } 23 | 24 | resp, err := driver.DismissAlert() 25 | if err != nil || resp.State != "success" { 26 | errorAndWrap(t, "Alert response was not correct", err) 27 | } 28 | 29 | printObjectResult(resp) 30 | } 31 | 32 | func Test_AlertDismissAlert_DismissingAnInvalidAlertResultsInAnError(t *testing.T) { 33 | setUp() 34 | defer tearDown() 35 | 36 | driver := createDriver(t) 37 | _, err := driver.CreateSession() 38 | if err != nil { 39 | errorAndWrap(t, "Error creating session", err) 40 | } 41 | 42 | _, err = driver.Go("https://google.com") 43 | if err != nil { 44 | errorAndWrap(t, "Error visiting URL", err) 45 | } 46 | 47 | resp, err := driver.DismissAlert() 48 | if err != nil { 49 | comErr := err.(goselenium.CommunicationError) 50 | if comErr.Response.State != goselenium.NoSuchAlert { 51 | errorAndWrap(t, "Incorrect result returned", err) 52 | } 53 | } else { 54 | errorAndWrap(t, "Incorrect result returned", err) 55 | } 56 | 57 | printObjectResult(resp) 58 | } 59 | -------------------------------------------------------------------------------- /test/integration_tests/alert_sendalerttext_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | goselenium "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_AlertSendAlertText_CanSendAlertTextCorrectly(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error creating session", err) 17 | } 18 | 19 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/prompt.html") 20 | if err != nil { 21 | errorAndWrap(t, "Error visiting URL", err) 22 | } 23 | 24 | resp, err := driver.SendAlertText("test") 25 | if err != nil || resp.State != "success" { 26 | errorAndWrap(t, "Error sending alert text", err) 27 | } 28 | 29 | _, err = driver.AcceptAlert() 30 | if err != nil { 31 | errorAndWrap(t, "Error accepting alert", err) 32 | } 33 | 34 | _, err = driver.AlertText() 35 | if err != nil { 36 | comErr := err.(goselenium.CommunicationError) 37 | if comErr.Response.State != goselenium.NoSuchAlert { 38 | errorAndWrap(t, "Error returned was not correct", err) 39 | } 40 | } else { 41 | errorAndWrap(t, "Error returned was not correct", err) 42 | } 43 | 44 | printObjectResult(resp) 45 | } 46 | -------------------------------------------------------------------------------- /test/integration_tests/command_closewindow_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CommandCloseWindow_CanCloseTheWindow(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | resp, err := driver.CloseWindow() 16 | if err != nil || resp.State != "success" || len(resp.Handles) > 0 { 17 | errorAndWrap(t, "Error was returned or response was not correct", err) 18 | } 19 | 20 | printObjectResult(resp) 21 | } 22 | -------------------------------------------------------------------------------- /test/integration_tests/command_maximizewindow_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CommandMaximizeWindow_CorrectResultIsReturned(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | resp, err := driver.MaximizeWindow() 16 | if err != nil || resp.State != "success" { 17 | errorAndWrap(t, "Error was returned or response was not correct", err) 18 | } 19 | 20 | printObjectResult(resp) 21 | } 22 | -------------------------------------------------------------------------------- /test/integration_tests/command_setwindowsize_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_CommandSetWindowSize_CorrectResponseIsReturned(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error thrown whilst creating session.", err) 17 | } 18 | 19 | dimensions := &goselenium.Dimensions{ 20 | Width: 600, 21 | Height: 400, 22 | } 23 | resp, err := driver.SetWindowSize(dimensions) 24 | if err != nil || resp.State != "success" { 25 | errorAndWrap(t, "Error was returned or response was not correct", err) 26 | } 27 | 28 | printObjectResult(resp) 29 | } 30 | -------------------------------------------------------------------------------- /test/integration_tests/command_switchtoframe_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_CommandSwitchToFrame_CorrectResponseIsReturnedByIndex(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error thrown whilst creating session.", err) 17 | } 18 | 19 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/iframe.html") 20 | if err != nil { 21 | errorAndWrap(t, "Error was thrown or result was not a success.", err) 22 | } 23 | 24 | resp, err := driver.SwitchToFrame(goselenium.ByIndex(0)) 25 | if err != nil || resp.State != "success" { 26 | errorAndWrap(t, "Error was thrown or result was not a success", err) 27 | } 28 | 29 | printObjectResult(resp) 30 | } 31 | 32 | func Test_CommandSwitchToFrame_InvalidByResultsInAnError(t *testing.T) { 33 | setUp() 34 | defer tearDown() 35 | 36 | driver := createDriver(t) 37 | _, err := driver.CreateSession() 38 | if err != nil { 39 | errorAndWrap(t, "Error thrown whilst creating session.", err) 40 | } 41 | 42 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/iframe.html") 43 | if err != nil { 44 | errorAndWrap(t, "Error was thrown or result was not a success.", err) 45 | } 46 | 47 | resp, err := driver.SwitchToFrame(goselenium.ByCSSSelector("iframe")) 48 | if err == nil { 49 | errorAndWrap(t, "Error was not thrown or was not the expected type.", err) 50 | } 51 | 52 | printObjectResult(resp) 53 | } 54 | -------------------------------------------------------------------------------- /test/integration_tests/command_switchtoparentframe_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_CommandSwitchToParentFrame_CorrectResponseCanBeReturned(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error thrown whilst creating session.", err) 17 | } 18 | 19 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/iframe.html") 20 | if err != nil { 21 | errorAndWrap(t, "Error was thrown whilst navigating.", err) 22 | } 23 | 24 | _, err = driver.SwitchToFrame(goselenium.ByIndex(0)) 25 | if err != nil { 26 | errorAndWrap(t, "Error was thrown whilst switching to frame 0.", err) 27 | } 28 | 29 | resp, err := driver.SwitchToParentFrame() 30 | if err != nil || resp.State != "success" { 31 | errorAndWrap(t, "Error was thrown or response was not a success.", err) 32 | } 33 | 34 | printObjectResult(resp) 35 | } 36 | -------------------------------------------------------------------------------- /test/integration_tests/command_windowhandle_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CommandWindowHandle_CorrectResponseIsReturned(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | resp, err := driver.WindowHandle() 16 | if err != nil || resp.State != "success" || resp.Handle == "" { 17 | errorAndWrap(t, "Error was returned or response was not correct", err) 18 | } 19 | 20 | printObjectResult(resp) 21 | } 22 | -------------------------------------------------------------------------------- /test/integration_tests/command_windowhandles_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CommandWindowHandles_WindowHandlesAreReturned(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | resp, err := driver.WindowHandles() 12 | if err != nil || resp.State != "success" || resp.Handles[0] == "" { 13 | errorAndWrap(t, "Error thrown or result was not what was expected.", err) 14 | } 15 | 16 | printObjectResult(resp) 17 | } 18 | -------------------------------------------------------------------------------- /test/integration_tests/command_windowsize_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CommandWindowSize_CorrectResultIsReturned(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | resp, err := driver.WindowSize() 16 | if err != nil || resp.State != "success" || resp.Dimensions.Width == 0 || resp.Dimensions.Height == 0 { 17 | errorAndWrap(t, "Error was returned or response was not correct", err) 18 | } 19 | 20 | printObjectResult(resp) 21 | } 22 | -------------------------------------------------------------------------------- /test/integration_tests/cookie_addcookie_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_CookieAddCookie_CanAddCookieWithCorrectFields(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error thrown whilst trying to create session.", err) 17 | } 18 | 19 | _, err = driver.Go("https://news.ycombinator.com") 20 | if err != nil { 21 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 22 | } 23 | 24 | resp, err := driver.AddCookie(&goselenium.Cookie{ 25 | Name: "cookie", 26 | Value: "cookieValue", 27 | Path: "/", 28 | Domain: ".ycombinator.com", 29 | SecureOnly: false, 30 | HTTPOnly: true, 31 | }) 32 | if err != nil || resp.State != "success" { 33 | errorAndWrap(t, "Error was thrown whilst retrieving cookies", err) 34 | } 35 | 36 | printObjectResult(resp) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /test/integration_tests/cookie_allcookies_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CookieAllCookies_CanRetrieveAllCookiesFromWebPage(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst trying to create session.", err) 13 | } 14 | 15 | _, err = driver.Go("https://news.ycombinator.com") 16 | if err != nil { 17 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 18 | } 19 | 20 | resp, err := driver.AllCookies() 21 | if err != nil || resp.State != "success" || resp.Cookies[0].Name == "" { 22 | errorAndWrap(t, "Error was thrown whilst retrieving cookies", err) 23 | } 24 | 25 | printObjectResult(resp) 26 | } 27 | -------------------------------------------------------------------------------- /test/integration_tests/cookie_cookie_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CookieCookie_CanRetrieveCookieFromWebPage(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst trying to create session.", err) 13 | } 14 | 15 | _, err = driver.Go("https://news.ycombinator.com") 16 | if err != nil { 17 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 18 | } 19 | 20 | resp, err := driver.Cookie("__cfduid") 21 | if err != nil || resp.State != "success" || resp.Cookie.Name == "" { 22 | errorAndWrap(t, "Error was thrown whilst retrieving cookie", err) 23 | } 24 | 25 | printObjectResult(resp) 26 | } 27 | -------------------------------------------------------------------------------- /test/integration_tests/cookie_deletecookie_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_CookieDeleteCookie_CanDeleteSpecifiedCookie(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | tests := []struct { 10 | url string 11 | cookieName string 12 | }{ 13 | { 14 | url: "https://news.ycombinator.com", 15 | cookieName: "__cfduid", 16 | }, 17 | { 18 | url: "https://www.google.com", 19 | cookieName: "CONSENT", 20 | }, 21 | } 22 | for _, te := range tests { 23 | driver := createDriver(t) 24 | _, err := driver.CreateSession() 25 | if err != nil { 26 | t.Errorf("Session creation failed") 27 | } 28 | 29 | _, err = driver.Go(te.url) 30 | if err != nil { 31 | t.Errorf("Navigation failed") 32 | } 33 | 34 | resp, err := driver.DeleteCookie(te.cookieName) 35 | if err != nil || resp.State != "success" { 36 | t.Errorf("Error whilst deleting cookie or was not a success.") 37 | } 38 | 39 | cookie, err := driver.Cookie(te.cookieName) 40 | if err != nil || cookie.Cookie.Name == te.cookieName { 41 | t.Errorf("Cookie still exists or an error occurred.") 42 | } 43 | 44 | driver.DeleteSession() 45 | 46 | printObjectResult(resp) 47 | } 48 | } 49 | 50 | func Test_CookieDeleteCookie_CanDeleteAllCookies(t *testing.T) { 51 | setUp() 52 | defer tearDown() 53 | 54 | driver := createDriver(t) 55 | _, err := driver.CreateSession() 56 | if err != nil { 57 | t.Errorf("Session creation failed") 58 | } 59 | 60 | _, err = driver.Go("https://www.google.com") 61 | if err != nil { 62 | t.Errorf("Navigation failed") 63 | } 64 | 65 | resp, err := driver.DeleteCookie("") 66 | if err != nil || resp.State != "success" { 67 | t.Errorf("Error whilst deleting cookie or was not a success.") 68 | } 69 | 70 | cookies, err := driver.AllCookies() 71 | if err != nil || len(cookies.Cookies) != 0 { 72 | t.Errorf("Cookies still exist or error was returned.") 73 | } 74 | 75 | printObjectResult(resp) 76 | } 77 | -------------------------------------------------------------------------------- /test/integration_tests/document_executescript_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_DocumentExecuteScript_CanExecuteScriptsSuccessfully(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | _, err = driver.Go("https://news.ycombinator.com") 16 | if err != nil { 17 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 18 | } 19 | 20 | resp, err := driver.ExecuteScript("return \"Test\";") 21 | if err != nil || resp.Response != "Test" { 22 | errorAndWrap(t, "Error was thrown whilst executing script or response was not correct", err) 23 | } 24 | 25 | printObjectResult(resp) 26 | } 27 | 28 | func Test_DocumentExecuteScriptAsync_CanExecuteScriptsSuccessfully(t *testing.T) { 29 | setUp() 30 | defer tearDown() 31 | 32 | driver := createDriver(t) 33 | _, err := driver.CreateSession() 34 | if err != nil { 35 | errorAndWrap(t, "Error thrown whilst creating session.", err) 36 | } 37 | 38 | _, err = driver.Go("https://news.ycombinator.com") 39 | if err != nil { 40 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 41 | } 42 | 43 | resp, err := driver.ExecuteScriptAsync("var callback = arguments[arguments.length - 1]; callback('Test');") 44 | if err != nil || resp.Response != "Test" { 45 | errorAndWrap(t, "Error was thrown whilst executing script or response was not correct", err) 46 | } 47 | 48 | printObjectResult(resp) 49 | } 50 | -------------------------------------------------------------------------------- /test/integration_tests/document_pagesource_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_DocumentPageSource_PageSourceIsCorrectlyRetrieved(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | _, err = driver.Go("https://news.ycombinator.com") 16 | if err != nil { 17 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 18 | } 19 | 20 | sauce, err := driver.PageSource() 21 | if err != nil || len(sauce.Source) == 0 { 22 | errorAndWrap(t, "Error was thrown or page source was empty", err) 23 | } 24 | 25 | printObjectResult(sauce) 26 | } 27 | -------------------------------------------------------------------------------- /test/integration_tests/element_attribute_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/bunsenapp/go-selenium" 8 | ) 9 | 10 | func Test_ElementAttribute_CanRetrieveAttributeCorrectly(t *testing.T) { 11 | setUp() 12 | defer tearDown() 13 | 14 | tests := []struct { 15 | url string 16 | by goselenium.By 17 | attr string 18 | val string 19 | }{ 20 | { 21 | url: "https://www.google.com", 22 | by: goselenium.ByCSSSelector("input#lst-ib"), 23 | attr: "maxlength", 24 | val: "2048", 25 | }, 26 | { 27 | url: "https://news.ycombinator.com", 28 | by: goselenium.ByCSSSelector("b.hnname > a"), 29 | attr: "href", 30 | val: "news", 31 | }, 32 | } 33 | for _, te := range tests { 34 | driver := createDriver(t) 35 | _, err := driver.CreateSession() 36 | if err != nil { 37 | errorAndWrap(t, "Error thrown whilst creating session.", err) 38 | } 39 | 40 | _, err = driver.Go(te.url) 41 | if err != nil { 42 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 43 | } 44 | 45 | el, err := driver.FindElement(te.by) 46 | if err != nil || el == nil { 47 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 48 | } 49 | 50 | att, err := el.Attribute(te.attr) 51 | if err != nil || !strings.Contains(att.Value, te.val) { 52 | errorAndWrap(t, "Error whilst retrieving attribute or attribute value was not correct", err) 53 | } 54 | 55 | driver.DeleteSession() 56 | 57 | printObjectResult(att) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/integration_tests/element_clear_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementClear_ElementsAreClearedSuccessfully(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | tests := []struct { 14 | url string 15 | by goselenium.By 16 | }{ 17 | { 18 | url: "https://bunsenapp.github.io/go-selenium/helpers/clear.html", 19 | by: goselenium.ByCSSSelector("input#should-clear"), 20 | }, 21 | } 22 | for _, te := range tests { 23 | driver := createDriver(t) 24 | _, err := driver.CreateSession() 25 | if err != nil { 26 | errorAndWrap(t, "Create session failed", err) 27 | } 28 | 29 | _, err = driver.Go(te.url) 30 | if err != nil { 31 | errorAndWrap(t, "Navigating to URL failed", err) 32 | } 33 | 34 | el, err := driver.FindElement(te.by) 35 | if err != nil { 36 | errorAndWrap(t, "Retrieving element failed", err) 37 | } 38 | 39 | _, err = el.Clear() 40 | if err != nil { 41 | errorAndWrap(t, "Clearing element failed", err) 42 | } 43 | 44 | resp, err := el.Text() 45 | if err != nil || len(resp.Text) > 0 { 46 | errorAndWrap(t, "Retrieving text failed or text was not cleared", err) 47 | } 48 | 49 | driver.DeleteSession() 50 | 51 | printObjectResult(resp) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/integration_tests/element_click_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/bunsenapp/go-selenium" 8 | ) 9 | 10 | func Test_ElementClick_ClickSuccessfullyNavigates(t *testing.T) { 11 | setUp() 12 | defer tearDown() 13 | 14 | tests := []struct { 15 | url string 16 | by goselenium.By 17 | expTitle string 18 | }{ 19 | { 20 | url: "https://www.google.com", 21 | by: goselenium.ByLinkText("Privacy"), 22 | expTitle: "Privacy Policy – Privacy & Terms – Google", 23 | }, 24 | { 25 | url: "https://news.ycombinator.com", 26 | by: goselenium.ByLinkText("new"), 27 | expTitle: "New Links | Hacker News", 28 | }, 29 | } 30 | for _, te := range tests { 31 | driver := createDriver(t) 32 | _, err := driver.CreateSession() 33 | if err != nil { 34 | errorAndWrap(t, "Error creating session.", err) 35 | } 36 | 37 | _, err = driver.Go(te.url) 38 | if err != nil { 39 | errorAndWrap(t, "Error visiting URL.", err) 40 | } 41 | 42 | el, err := driver.FindElement(te.by) 43 | if err != nil { 44 | errorAndWrap(t, "Error finding element", err) 45 | } 46 | 47 | _, err = el.Click() 48 | if err != nil { 49 | errorAndWrap(t, "Error clicking element", err) 50 | } 51 | 52 | // TODO: Unfortunate Selenium flaw - will need the helpers to get around 53 | // an explicit wait. Also occurs in Python library. 54 | time.Sleep(1 * time.Second) 55 | 56 | resp, err := driver.Title() 57 | if err != nil || resp.Title != te.expTitle { 58 | errorAndWrap(t, "Error retrieving title or title was not correct", err) 59 | } 60 | 61 | driver.DeleteSession() 62 | 63 | printObjectResult(resp) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/integration_tests/element_cssclass_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/bunsenapp/go-selenium" 9 | ) 10 | 11 | func Test_ElementCSSValue_CanGetCorrectCSSValue(t *testing.T) { 12 | setUp() 13 | defer tearDown() 14 | 15 | tests := []struct { 16 | url string 17 | by goselenium.By 18 | cssName string 19 | cssVal string 20 | }{ 21 | { 22 | url: "https://www.google.com", 23 | by: goselenium.ByCSSSelector("input[name='btnK']"), 24 | cssName: "font-family", 25 | cssVal: "arial,sans-serif", 26 | }, 27 | { 28 | url: "https://news.ycombinator.com", 29 | by: goselenium.ByCSSSelector("a[href='news']"), 30 | cssName: "color", 31 | cssVal: "rgb(0, 0, 0)", 32 | }, 33 | } 34 | for _, te := range tests { 35 | driver := createDriver(t) 36 | _, err := driver.CreateSession() 37 | if err != nil { 38 | errorAndWrap(t, "Error thrown whilst creating session.", err) 39 | } 40 | 41 | _, err = driver.Go(te.url) 42 | if err != nil { 43 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 44 | } 45 | 46 | el, err := driver.FindElement(te.by) 47 | if err != nil || el == nil { 48 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 49 | } 50 | 51 | att, err := el.CSSValue(te.cssName) 52 | if err != nil || !strings.Contains(att.Value, te.cssVal) { 53 | fmt.Println(att) 54 | errorAndWrap(t, "Error whilst retrieving CSS class or value was not correct", err) 55 | } 56 | 57 | driver.DeleteSession() 58 | 59 | printObjectResult(att) 60 | } 61 | } 62 | 63 | func Test_ElementCSSValue_CSSValueThatDoesNotExistDoesNotCauseAnError(t *testing.T) { 64 | setUp() 65 | defer tearDown() 66 | 67 | tests := []struct { 68 | url string 69 | by goselenium.By 70 | cssName string 71 | }{ 72 | { 73 | url: "https://www.google.com", 74 | by: goselenium.ByCSSSelector("input[name='btnK']"), 75 | cssName: "background", 76 | }, 77 | { 78 | url: "https://news.ycombinator.com", 79 | by: goselenium.ByCSSSelector("a[href='news']"), 80 | cssName: "border-radius", 81 | }, 82 | } 83 | for _, te := range tests { 84 | driver := createDriver(t) 85 | _, err := driver.CreateSession() 86 | if err != nil { 87 | errorAndWrap(t, "Error thrown whilst creating session.", err) 88 | } 89 | 90 | _, err = driver.Go(te.url) 91 | if err != nil { 92 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 93 | } 94 | 95 | el, err := driver.FindElement(te.by) 96 | if err != nil || el == nil { 97 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 98 | } 99 | 100 | att, err := el.CSSValue(te.cssName) 101 | if err != nil { 102 | fmt.Println(att) 103 | errorAndWrap(t, "Error whilst retrieving CSS class", err) 104 | } 105 | 106 | driver.DeleteSession() 107 | 108 | printObjectResult(att) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/integration_tests/element_enabled_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementEnabled_EnabledElementIsReturnedCorrectly(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error thrown whilst creating session.", err) 17 | } 18 | 19 | _, err = driver.Go("https://google.com") 20 | if err != nil { 21 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 22 | } 23 | 24 | el, err := driver.FindElement(goselenium.ByCSSSelector("input#lst-ib")) 25 | if err != nil || el == nil { 26 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 27 | } 28 | 29 | resp, err := el.Enabled() 30 | if err != nil || !resp.Enabled { 31 | errorAndWrap(t, "Error whilst retrieving response or element was not enabled", err) 32 | } 33 | 34 | printObjectResult(resp) 35 | } 36 | 37 | func Test_ElementEnabled_DisabledElementIsReturnedCorrectly(t *testing.T) { 38 | setUp() 39 | defer tearDown() 40 | 41 | tests := []struct { 42 | url string 43 | by goselenium.By 44 | exp bool 45 | }{ 46 | { 47 | url: "https://news.ycombinator.com", 48 | by: goselenium.ByCSSSelector("a[href='news']"), 49 | exp: true, 50 | }, 51 | { 52 | url: "https://bunsenapp.github.io/go-selenium/helpers/disabled.html", 53 | by: goselenium.ByCSSSelector("input"), 54 | exp: false, 55 | }, 56 | } 57 | for _, te := range tests { 58 | driver := createDriver(t) 59 | _, err := driver.CreateSession() 60 | if err != nil { 61 | errorAndWrap(t, "Error thrown whilst creating session.", err) 62 | } 63 | 64 | _, err = driver.Go(te.url) 65 | if err != nil { 66 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 67 | } 68 | 69 | el, err := driver.FindElement(te.by) 70 | if err != nil || el == nil { 71 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 72 | } 73 | 74 | resp, err := el.Enabled() 75 | if err != nil || resp.Enabled != te.exp { 76 | errorAndWrap(t, "Error whilst retrieving response or the element's enabled property was not correct", err) 77 | } 78 | 79 | driver.DeleteSession() 80 | 81 | printObjectResult(resp) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /test/integration_tests/element_findelement_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementFindElement_CanFindElementByCSSSelector(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | tests := []struct { 14 | url string 15 | by goselenium.By 16 | }{ 17 | {"https://www.google.com", goselenium.ByCSSSelector("input#lst-ib")}, 18 | {"https://www.reddit.com", goselenium.ByCSSSelector("input[name=q]")}, 19 | {"https://news.ycombinator.com", goselenium.ByCSSSelector("#hnmain")}, 20 | } 21 | for _, te := range tests { 22 | driver := createDriver(t) 23 | _, err := driver.CreateSession() 24 | if err != nil { 25 | errorAndWrap(t, "Error thrown whilst creating session.", err) 26 | } 27 | 28 | _, err = driver.Go(te.url) 29 | if err != nil { 30 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 31 | } 32 | 33 | el, err := driver.FindElement(te.by) 34 | if err != nil || el == nil { 35 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 36 | } 37 | 38 | printObjectResult(el) 39 | 40 | driver.DeleteSession() 41 | } 42 | } 43 | 44 | func Test_ElementFindElement_CanFindElementByLinkText(t *testing.T) { 45 | setUp() 46 | defer tearDown() 47 | 48 | tests := []struct { 49 | url string 50 | by goselenium.By 51 | }{ 52 | {"https://www.google.com", goselenium.ByLinkText("Gmail")}, 53 | {"https://www.reddit.com", goselenium.ByLinkText("new")}, 54 | {"https://news.ycombinator.com", goselenium.ByLinkText("submit")}, 55 | } 56 | for _, te := range tests { 57 | driver := createDriver(t) 58 | _, err := driver.CreateSession() 59 | if err != nil { 60 | errorAndWrap(t, "Error thrown whilst creating session.", err) 61 | } 62 | 63 | _, err = driver.Go(te.url) 64 | if err != nil { 65 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 66 | } 67 | 68 | el, err := driver.FindElement(te.by) 69 | if err != nil || el == nil { 70 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 71 | } 72 | 73 | printObjectResult(el) 74 | 75 | driver.DeleteSession() 76 | } 77 | } 78 | 79 | func Test_ElementFindElement_CanFindElementByPartialLinkText(t *testing.T) { 80 | setUp() 81 | defer tearDown() 82 | 83 | tests := []struct { 84 | url string 85 | by goselenium.By 86 | }{ 87 | {"https://www.google.com", goselenium.ByPartialLinkText("Gmai")}, 88 | {"https://www.reddit.com", goselenium.ByPartialLinkText("ew")}, 89 | {"https://news.ycombinator.com", goselenium.ByPartialLinkText("ubmit")}, 90 | } 91 | for _, te := range tests { 92 | driver := createDriver(t) 93 | _, err := driver.CreateSession() 94 | if err != nil { 95 | errorAndWrap(t, "Error thrown whilst creating session.", err) 96 | } 97 | 98 | _, err = driver.Go(te.url) 99 | if err != nil { 100 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 101 | } 102 | 103 | el, err := driver.FindElement(te.by) 104 | if err != nil || el == nil { 105 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 106 | } 107 | 108 | printObjectResult(el) 109 | 110 | driver.DeleteSession() 111 | } 112 | } 113 | 114 | func Test_ElementFindElement_CanFindElementByXPath(t *testing.T) { 115 | setUp() 116 | defer tearDown() 117 | 118 | tests := []struct { 119 | url string 120 | by goselenium.By 121 | }{ 122 | {"https://www.google.com", goselenium.ByXPath("//input[@id='lst-ib']")}, 123 | {"https://news.ycombinator.com", goselenium.ByXPath("//b[@class='hnname']/a[@href='news']")}, 124 | } 125 | for _, te := range tests { 126 | driver := createDriver(t) 127 | _, err := driver.CreateSession() 128 | if err != nil { 129 | errorAndWrap(t, "Error thrown whilst creating session.", err) 130 | } 131 | 132 | _, err = driver.Go(te.url) 133 | if err != nil { 134 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 135 | } 136 | 137 | el, err := driver.FindElement(te.by) 138 | if err != nil || el == nil { 139 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 140 | } 141 | 142 | printObjectResult(el) 143 | 144 | driver.DeleteSession() 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/integration_tests/element_findelements_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementFindElements_CanFindElementsByCSSSelector(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | tests := []struct { 14 | url string 15 | by goselenium.By 16 | }{ 17 | {"https://www.google.com", goselenium.ByCSSSelector("input")}, 18 | {"https://www.reddit.com", goselenium.ByCSSSelector("a")}, 19 | {"https://news.ycombinator.com", goselenium.ByCSSSelector(".storylink")}, 20 | } 21 | for _, te := range tests { 22 | driver := createDriver(t) 23 | _, err := driver.CreateSession() 24 | if err != nil { 25 | errorAndWrap(t, "Error thrown whilst creating session.", err) 26 | } 27 | 28 | _, err = driver.Go(te.url) 29 | if err != nil { 30 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 31 | } 32 | 33 | el, err := driver.FindElements(te.by) 34 | if err != nil || el == nil || len(el) <= 1 { 35 | errorAndWrap(t, "Error whilst finding elements or element was not found", err) 36 | } 37 | 38 | printObjectResult(el) 39 | 40 | driver.DeleteSession() 41 | } 42 | } 43 | 44 | func Test_ElementFindElements_CanFindElementsByLinkText(t *testing.T) { 45 | setUp() 46 | defer tearDown() 47 | 48 | tests := []struct { 49 | url string 50 | by goselenium.By 51 | }{ 52 | {"https://www.reddit.com", goselenium.ByLinkText("share")}, 53 | {"https://news.ycombinator.com", goselenium.ByLinkText("hide")}, 54 | } 55 | for _, te := range tests { 56 | driver := createDriver(t) 57 | _, err := driver.CreateSession() 58 | if err != nil { 59 | errorAndWrap(t, "Error thrown whilst creating session.", err) 60 | } 61 | 62 | _, err = driver.Go(te.url) 63 | if err != nil { 64 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 65 | } 66 | 67 | el, err := driver.FindElements(te.by) 68 | if err != nil || el == nil || len(el) <= 1 { 69 | errorAndWrap(t, "Error whilst finding elements or elements were not found", err) 70 | } 71 | 72 | printObjectResult(el) 73 | 74 | driver.DeleteSession() 75 | } 76 | } 77 | 78 | func Test_ElementFindElements_CanFindElementsByPartialLinkText(t *testing.T) { 79 | setUp() 80 | defer tearDown() 81 | 82 | tests := []struct { 83 | url string 84 | by goselenium.By 85 | }{ 86 | {"https://www.google.com", goselenium.ByPartialLinkText("a")}, 87 | {"https://www.reddit.com", goselenium.ByPartialLinkText("hi")}, 88 | {"https://news.ycombinator.com", goselenium.ByPartialLinkText("comment")}, 89 | } 90 | for _, te := range tests { 91 | driver := createDriver(t) 92 | _, err := driver.CreateSession() 93 | if err != nil { 94 | errorAndWrap(t, "Error thrown whilst creating session.", err) 95 | } 96 | 97 | _, err = driver.Go(te.url) 98 | if err != nil { 99 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 100 | } 101 | 102 | el, err := driver.FindElements(te.by) 103 | if err != nil || el == nil || len(el) <= 1 { 104 | errorAndWrap(t, "Error whilst finding elements or elements were not found", err) 105 | } 106 | 107 | printObjectResult(el) 108 | 109 | driver.DeleteSession() 110 | } 111 | } 112 | 113 | func Test_ElementFindElements_CanFindElementsByXPath(t *testing.T) { 114 | setUp() 115 | defer tearDown() 116 | 117 | tests := []struct { 118 | url string 119 | by goselenium.By 120 | }{ 121 | {"https://www.google.com", goselenium.ByXPath("//input")}, 122 | {"https://news.ycombinator.com", goselenium.ByXPath("//a")}, 123 | } 124 | for _, te := range tests { 125 | driver := createDriver(t) 126 | _, err := driver.CreateSession() 127 | if err != nil { 128 | errorAndWrap(t, "Error thrown whilst creating session.", err) 129 | } 130 | 131 | _, err = driver.Go(te.url) 132 | if err != nil { 133 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 134 | } 135 | 136 | el, err := driver.FindElements(te.by) 137 | if err != nil || el == nil { 138 | errorAndWrap(t, "Error whilst finding elements or element were not found", err) 139 | } 140 | 141 | printObjectResult(el) 142 | 143 | driver.DeleteSession() 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /test/integration_tests/element_rectangle_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementRectangle_SizeIsReturnedCorrectly(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error thrown whilst creating session.", err) 17 | } 18 | 19 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/size.html") 20 | if err != nil { 21 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 22 | } 23 | 24 | el, err := driver.FindElement(goselenium.ByCSSSelector("div")) 25 | if err != nil || el == nil { 26 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 27 | } 28 | 29 | resp, err := el.Rectangle() 30 | if err != nil || resp.Rectangle.Width != 100 || resp.Rectangle.Height != 100 { 31 | errorAndWrap(t, "Error was returned or element's size was not correct.", err) 32 | } 33 | 34 | printObjectResult(resp) 35 | } 36 | 37 | func Test_ElementRectangle_PositionIsReturnedCorrectly(t *testing.T) { 38 | setUp() 39 | defer tearDown() 40 | 41 | driver := createDriver(t) 42 | _, err := driver.CreateSession() 43 | if err != nil { 44 | errorAndWrap(t, "Error thrown whilst creating session.", err) 45 | } 46 | 47 | _, err = driver.Go("https://google.com") 48 | if err != nil { 49 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 50 | } 51 | 52 | el, err := driver.FindElement(goselenium.ByCSSSelector("input#lst-ib")) 53 | if err != nil || el == nil { 54 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 55 | } 56 | 57 | resp, err := el.Rectangle() 58 | if err != nil || resp.Rectangle.X == 0 || resp.Rectangle.Height == 0 { 59 | errorAndWrap(t, "Error was returned or element's size was not correct.", err) 60 | } 61 | 62 | printObjectResult(resp) 63 | } 64 | -------------------------------------------------------------------------------- /test/integration_tests/element_selected_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementSelected_CheckedElementReturnsCorrectly(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | tests := []goselenium.By{ 14 | goselenium.ByCSSSelector("input[name='i_am_checked']"), 15 | goselenium.ByCSSSelector("input[name='i_am_selected']"), 16 | } 17 | for _, te := range tests { 18 | driver := createDriver(t) 19 | _, err := driver.CreateSession() 20 | if err != nil { 21 | errorAndWrap(t, "Error thrown whilst creating session.", err) 22 | } 23 | 24 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/selected.html") 25 | if err != nil { 26 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 27 | } 28 | 29 | el, err := driver.FindElement(te) 30 | if err != nil || el == nil { 31 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 32 | } 33 | 34 | resp, err := el.Selected() 35 | if err != nil || !resp.Selected { 36 | errorAndWrap(t, "Error was returned or element was not selected.", err) 37 | } 38 | 39 | driver.DeleteSession() 40 | 41 | printObjectResult(resp) 42 | } 43 | } 44 | 45 | func Test_ElementSelected_UncheckedElementReturnsCorrectly(t *testing.T) { 46 | setUp() 47 | defer tearDown() 48 | 49 | tests := []goselenium.By{ 50 | goselenium.ByCSSSelector("input[name='i_am_not_checked']"), 51 | goselenium.ByCSSSelector("input[name='i_am_not_selected']"), 52 | } 53 | for _, te := range tests { 54 | driver := createDriver(t) 55 | _, err := driver.CreateSession() 56 | if err != nil { 57 | errorAndWrap(t, "Error thrown whilst creating session.", err) 58 | } 59 | 60 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/selected.html") 61 | if err != nil { 62 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 63 | } 64 | 65 | el, err := driver.FindElement(te) 66 | if err != nil || el == nil { 67 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 68 | } 69 | 70 | resp, err := el.Selected() 71 | if err != nil || resp.Selected { 72 | errorAndWrap(t, "Error was returned or element was selected.", err) 73 | } 74 | 75 | driver.DeleteSession() 76 | 77 | printObjectResult(resp) 78 | } 79 | } 80 | 81 | func Test_ElementSelected_RandomElementsDoNotError(t *testing.T) { 82 | setUp() 83 | defer tearDown() 84 | 85 | tests := []goselenium.By{ 86 | goselenium.ByCSSSelector("a.storylink"), 87 | goselenium.ByCSSSelector(".title"), 88 | } 89 | for _, te := range tests { 90 | driver := createDriver(t) 91 | _, err := driver.CreateSession() 92 | if err != nil { 93 | errorAndWrap(t, "Error thrown whilst creating session.", err) 94 | } 95 | 96 | _, err = driver.Go("https://news.ycombinator.com") 97 | if err != nil { 98 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 99 | } 100 | 101 | el, err := driver.FindElement(te) 102 | if err != nil || el == nil { 103 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 104 | } 105 | 106 | resp, err := el.Selected() 107 | if err != nil || resp.Selected { 108 | errorAndWrap(t, "Error was returned or element was selected.", err) 109 | } 110 | 111 | driver.DeleteSession() 112 | 113 | printObjectResult(resp) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /test/integration_tests/element_sendkeys_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/bunsenapp/go-selenium" 8 | ) 9 | 10 | func Test_ElementSendKeys_CanSendKeysToInputField(t *testing.T) { 11 | setUp() 12 | defer tearDown() 13 | 14 | driver := createDriver(t) 15 | _, err := driver.CreateSession() 16 | if err != nil { 17 | errorAndWrap(t, "Error creating session", err) 18 | } 19 | 20 | _, err = driver.Go("https://www.google.com") 21 | if err != nil { 22 | errorAndWrap(t, "Error navigating to URL", err) 23 | } 24 | 25 | el, err := driver.FindElement(goselenium.ByCSSSelector("input#lst-ib")) 26 | if err != nil { 27 | errorAndWrap(t, "Error finding element", err) 28 | } 29 | 30 | _, err = el.SendKeys("test") 31 | if err != nil { 32 | errorAndWrap(t, "Error sending keys to element", err) 33 | } 34 | 35 | _, err = el.SendKeys(goselenium.EnterKey) 36 | if err != nil { 37 | errorAndWrap(t, "Error sending enter key to element", err) 38 | } 39 | 40 | url, err := driver.CurrentURL() 41 | if err != nil || !strings.Contains(url.URL, "test") { 42 | errorAndWrap(t, "Error retrieving current URL or it did not contain the correct value", err) 43 | } 44 | 45 | printObjectResult(url) 46 | } 47 | -------------------------------------------------------------------------------- /test/integration_tests/element_tagname_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementTagName_CanRetrieveCorrectTagName(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | tests := []struct { 14 | url string 15 | by goselenium.By 16 | exp string 17 | }{ 18 | { 19 | url: "https://news.ycombinator.com", 20 | by: goselenium.ByCSSSelector("a[href='submit']"), 21 | exp: "a", 22 | }, 23 | { 24 | url: "https://google.com", 25 | by: goselenium.ByCSSSelector("input#lst-ib"), 26 | exp: "input", 27 | }, 28 | } 29 | for _, te := range tests { 30 | driver := createDriver(t) 31 | _, err := driver.CreateSession() 32 | if err != nil { 33 | errorAndWrap(t, "Error thrown whilst creating session.", err) 34 | } 35 | 36 | _, err = driver.Go(te.url) 37 | if err != nil { 38 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 39 | } 40 | 41 | el, err := driver.FindElement(te.by) 42 | if err != nil || el == nil { 43 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 44 | } 45 | 46 | resp, err := el.TagName() 47 | if err != nil || resp.Tag != te.exp { 48 | errorAndWrap(t, "Error was returned or tag name was incorrect.", err) 49 | } 50 | 51 | driver.DeleteSession() 52 | 53 | printObjectResult(resp) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/integration_tests/element_text_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_ElementText_CorrectElementTextGetsReturned(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | tests := []struct { 14 | url string 15 | by goselenium.By 16 | exp string 17 | }{ 18 | { 19 | url: "https://google.com", 20 | by: goselenium.ByCSSSelector("input#lst-ib"), 21 | exp: "", 22 | }, 23 | { 24 | url: "https://news.ycombinator.com", 25 | by: goselenium.ByCSSSelector("a[href='submit']"), 26 | exp: "submit", 27 | }, 28 | } 29 | for _, te := range tests { 30 | driver := createDriver(t) 31 | _, err := driver.CreateSession() 32 | if err != nil { 33 | errorAndWrap(t, "Error thrown whilst creating session.", err) 34 | } 35 | 36 | _, err = driver.Go(te.url) 37 | if err != nil { 38 | errorAndWrap(t, "Error thrown whilst visiting url.", err) 39 | } 40 | 41 | el, err := driver.FindElement(te.by) 42 | if err != nil || el == nil { 43 | errorAndWrap(t, "Error whilst finding element or element was not found", err) 44 | } 45 | 46 | txt, err := el.Text() 47 | if err != nil || txt.Text != te.exp { 48 | errorAndWrap(t, "Error whilst getting element text or text was not correct", err) 49 | } 50 | 51 | driver.DeleteSession() 52 | 53 | printObjectResult(txt) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/integration_tests/helpers_elementpresent_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | goselenium "github.com/bunsenapp/go-selenium" 8 | ) 9 | 10 | func Test_ElementWaitUntilElementPresent_CanSucceed(t *testing.T) { 11 | setUp() 12 | defer tearDown() 13 | 14 | driver := createDriver(t) 15 | _, err := driver.CreateSession() 16 | if err != nil { 17 | errorAndWrap(t, "Error creating session", err) 18 | } 19 | 20 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/element-present.html") 21 | if err != nil { 22 | errorAndWrap(t, "Error navigating to URL", err) 23 | } 24 | 25 | by := goselenium.ByCSSSelector("#not-present-div") 26 | resp := driver.Wait(goselenium.UntilElementPresent(by), 20*time.Second, 0) 27 | if !resp { 28 | errorAndWrap(t, "Error waiting for element to be visible or it timed out", err) 29 | } 30 | 31 | printObjectResult(resp) 32 | } 33 | 34 | func Test_ElementWaitUntilElementPresent_NotFoundPriorToTimeoutFails(t *testing.T) { 35 | setUp() 36 | defer tearDown() 37 | 38 | driver := createDriver(t) 39 | _, err := driver.CreateSession() 40 | if err != nil { 41 | errorAndWrap(t, "Error creating session", err) 42 | } 43 | 44 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/element-present.html") 45 | if err != nil { 46 | errorAndWrap(t, "Error navigating to URL", err) 47 | } 48 | 49 | by := goselenium.ByCSSSelector("#not-present-div") 50 | resp := driver.Wait(goselenium.UntilElementPresent(by), 1*time.Second, 0) 51 | if resp { 52 | errorAndWrap(t, "Error was not thrown when it should have been", err) 53 | } 54 | 55 | printObjectResult(resp) 56 | } 57 | -------------------------------------------------------------------------------- /test/integration_tests/helpers_untilurlis_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | goselenium "github.com/bunsenapp/go-selenium" 8 | ) 9 | 10 | func Test_WaitUntilURLIs_WorksCorrectly(t *testing.T) { 11 | setUp() 12 | defer tearDown() 13 | 14 | driver := createDriver(t) 15 | _, err := driver.CreateSession() 16 | if err != nil { 17 | errorAndWrap(t, "Error whilst creating session", err) 18 | } 19 | 20 | _, err = driver.Go("https://bunsenapp.github.io/go-selenium/helpers/url-change.html") 21 | if err != nil { 22 | errorAndWrap(t, "Error whilst navigating", err) 23 | } 24 | 25 | ok := driver.Wait(goselenium.UntilURLIs("https://bunsenapp.github.io/test"), 30*time.Second, 0) 26 | if !ok { 27 | errorAndWrap(t, "Timeout was exceeded", nil) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/integration_tests/navigate_back_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func Test_NavigateBack_NavigateBackWorksCorrectly(t *testing.T) { 9 | setUp() 10 | defer tearDown() 11 | 12 | driver := createDriver(t) 13 | _, err := driver.CreateSession() 14 | if err != nil { 15 | errorAndWrap(t, "Error thrown whilst creating session.", err) 16 | } 17 | 18 | goResp, err := driver.Go("https://news.ycombinator.com") 19 | if err != nil || goResp.State != "success" { 20 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 21 | } 22 | 23 | goResp, err = driver.Go("https://google.co.uk") 24 | if err != nil || goResp.State != "success" { 25 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 26 | } 27 | 28 | backResp, err := driver.Back() 29 | if err != nil || backResp.State != "success" { 30 | errorAndWrap(t, "Error was thrown whilst navigating back or results was not a success.", err) 31 | } 32 | 33 | currentURLResp, err := driver.CurrentURL() 34 | if err != nil || !strings.HasPrefix(currentURLResp.URL, "https://news.ycombinator.com") { 35 | errorAndWrap(t, "Error was thrown or URL was not what it should have been.", err) 36 | } 37 | 38 | printObjectResult(currentURLResp) 39 | } 40 | -------------------------------------------------------------------------------- /test/integration_tests/navigate_forward_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func Test_NavigateForward_NavigateFowardWorksCorrectly(t *testing.T) { 9 | setUp() 10 | defer tearDown() 11 | 12 | driver := createDriver(t) 13 | _, err := driver.CreateSession() 14 | if err != nil { 15 | errorAndWrap(t, "Error thrown whilst creating session.", err) 16 | } 17 | 18 | goResp, err := driver.Go("https://google.co.uk") 19 | if err != nil || goResp.State != "success" { 20 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 21 | } 22 | 23 | goResp, err = driver.Go("https://news.ycombinator.com") 24 | if err != nil || goResp.State != "success" { 25 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 26 | } 27 | 28 | backResp, err := driver.Back() 29 | if err != nil || backResp.State != "success" { 30 | errorAndWrap(t, "Error was thrown whilst navigating backwards or results was not a success.", err) 31 | } 32 | 33 | forwardResp, err := driver.Forward() 34 | if err != nil || forwardResp.State != "success" { 35 | errorAndWrap(t, "Error was thrown whilst navigating forwards or result was not a success.", err) 36 | } 37 | 38 | currentURLResp, err := driver.CurrentURL() 39 | if err != nil || !strings.HasPrefix(currentURLResp.URL, "https://news.ycombinator.com") { 40 | errorAndWrap(t, "Error was thrown or URL was not what it should have been.", err) 41 | } 42 | 43 | printObjectResult(currentURLResp) 44 | } 45 | -------------------------------------------------------------------------------- /test/integration_tests/navigate_go_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_NavigateGo_CanNavigateSuccessfully(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.CreateSession() 15 | if err != nil { 16 | errorAndWrap(t, "Error thrown whilst creating session.", err) 17 | } 18 | 19 | resp, err := driver.Go("https://www.google.com") 20 | if err != nil || resp.State != "success" { 21 | errorAndWrap(t, "Error was thrown or result was not a success.", err) 22 | } 23 | } 24 | 25 | func Test_NavigateGo_InvalidURLIsReturned(t *testing.T) { 26 | setUp() 27 | defer tearDown() 28 | 29 | driver := createDriver(t) 30 | _, err := driver.CreateSession() 31 | if err != nil { 32 | errorAndWrap(t, "Error thrown whilst creating session.", err) 33 | } 34 | 35 | resp, err := driver.Go("www.google.com") 36 | if err != nil { 37 | if !goselenium.IsInvalidURLError(err) { 38 | errorAndWrap(t, "Error was thrown or result was not a success.", err) 39 | } 40 | } 41 | 42 | printObjectResult(resp) 43 | } 44 | 45 | func Test_NavigateGo_CanGetCurrentURL(t *testing.T) { 46 | setUp() 47 | defer tearDown() 48 | 49 | driver := createDriver(t) 50 | _, err := driver.CreateSession() 51 | if err != nil { 52 | errorAndWrap(t, "Error thrown whilst creating session.", err) 53 | } 54 | 55 | _, err = driver.Go("https://github.com/") 56 | if err != nil { 57 | errorAndWrap(t, "Error was thrown when it shouldn't have been.", err) 58 | } 59 | 60 | resp, err := driver.CurrentURL() 61 | if err != nil || resp.URL != "https://github.com/" { 62 | errorAndWrap(t, "Error was thrown or URL was not what was sent.", err) 63 | } 64 | 65 | printObjectResult(resp) 66 | } 67 | -------------------------------------------------------------------------------- /test/integration_tests/navigate_refresh_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func Test_NavigateRefresh_RefreshWorksCorrectly(t *testing.T) { 9 | setUp() 10 | defer tearDown() 11 | 12 | driver := createDriver(t) 13 | _, err := driver.CreateSession() 14 | if err != nil { 15 | errorAndWrap(t, "Error thrown whilst creating session.", err) 16 | } 17 | 18 | goResp, err := driver.Go("https://news.ycombinator.com") 19 | if err != nil || goResp.State != "success" { 20 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 21 | } 22 | 23 | refreshResp, err := driver.Refresh() 24 | if err != nil || refreshResp.State != "success" { 25 | errorAndWrap(t, "Error was thrown whilst refreshing or result was not a success.", err) 26 | } 27 | 28 | currentURLResp, err := driver.CurrentURL() 29 | if err != nil || !strings.HasPrefix(currentURLResp.URL, "https://news.ycombinator.com") { 30 | errorAndWrap(t, "Error was thrown or URL was not what it should have been.", err) 31 | } 32 | 33 | printObjectResult(currentURLResp) 34 | } 35 | -------------------------------------------------------------------------------- /test/integration_tests/navigate_title_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_NavigateTitle_TitleCanBeRetrievedSuccessfully(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | goResp, err := driver.Go("https://google.com") 16 | if err != nil || goResp.State != "success" { 17 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 18 | } 19 | 20 | titleResponse, err := driver.Title() 21 | if err != nil || titleResponse.Title != "Google" { 22 | errorAndWrap(t, "Error was thrown or URL was not what it should have been.", err) 23 | } 24 | 25 | printObjectResult(titleResponse) 26 | } 27 | -------------------------------------------------------------------------------- /test/integration_tests/screenshot_screenshot_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_ScreenshotScreenshot_ScreenshotCanBeTakenSuccessfully(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Error thrown whilst creating session.", err) 13 | } 14 | 15 | goResp, err := driver.Go("https://google.com") 16 | if err != nil || goResp.State != "success" { 17 | errorAndWrap(t, "Error was thrown whilst navigating or result was not a success.", err) 18 | } 19 | 20 | scs, err := driver.Screenshot() 21 | if err != nil || scs.State != "success" || len(scs.EncodedImage) == 0 { 22 | errorAndWrap(t, "Error thrown whilst taking screenshot.", err) 23 | } 24 | 25 | dec, err := scs.ImageBytes() 26 | if err != nil || len(dec) == 0 { 27 | errorAndWrap(t, "Error thrown whilst getting image bytes of screenshot.", err) 28 | } 29 | 30 | printObjectResult(dec) 31 | } 32 | -------------------------------------------------------------------------------- /test/integration_tests/session_create_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func Test_SessionCreate_ANewSessionCanBeCreated(t *testing.T) { 9 | setUp() 10 | defer tearDown() 11 | 12 | driver := createDriver(t) 13 | session, err := driver.CreateSession() 14 | if err != nil { 15 | errorAndWrap(t, "Creation of session yielded an error.", err) 16 | } else if session.SessionID == "" || session.Capabilities.BrowserName != "firefox" { 17 | errorAndWrap(t, "Returned object was not set correctly.", err) 18 | } 19 | 20 | printObjectResult(session) 21 | } 22 | 23 | func Test_SessionCreate_TrailingSlashIsAdded(t *testing.T) { 24 | setUp() 25 | defer tearDown() 26 | 27 | driver := createDriver(t) 28 | _, err := driver.CreateSession() 29 | if strings.HasSuffix(driver.DriverURL(), "/") || err != nil { 30 | errorAndWrap(t, "Driver URL did not get a slash appended or an error occurred.", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/integration_tests/session_delete_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_SessionDelete_CallingDeleteSessionMethodWithoutASessionIdResultsInAnError(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.DeleteSession() 15 | if err == nil || !goselenium.IsSessionIDError(err) { 16 | errorAndWrap(t, "Session error was not returned when it should have been.", err) 17 | } 18 | } 19 | 20 | func Test_SessionDelete_DeleteSessionMethodWorksCorrectly(t *testing.T) { 21 | setUp() 22 | defer tearDown() 23 | 24 | driver := createDriver(t) 25 | _, err := driver.CreateSession() 26 | if err != nil { 27 | errorAndWrap(t, "Error returned whilst creating session", err) 28 | } 29 | 30 | resp, err := driver.DeleteSession() 31 | if err != nil || resp.State != "success" { 32 | errorAndWrap(t, "Error returned or state was not success whilst deleting session.", err) 33 | } 34 | 35 | printObjectResult(resp) 36 | } 37 | -------------------------------------------------------------------------------- /test/integration_tests/session_general_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_Session_CanCreateAndDeleteSession(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | _, err := driver.CreateSession() 11 | if err != nil { 12 | errorAndWrap(t, "Creation of session yielded an error.", err) 13 | } 14 | 15 | _, err = driver.DeleteSession() 16 | if err != nil { 17 | errorAndWrap(t, "Creation of session yielded an error.", err) 18 | } 19 | } 20 | 21 | func Test_Session_CanCreateSessionAndGetStatus(t *testing.T) { 22 | setUp() 23 | defer tearDown() 24 | 25 | driver := createDriver(t) 26 | _, err := driver.CreateSession() 27 | if err != nil { 28 | errorAndWrap(t, "Creation of session yielded an error.", err) 29 | } 30 | 31 | resp, err := driver.SessionStatus() 32 | if err != nil || resp.State != "success" { 33 | errorAndWrap(t, "Error occurred or the state was not a success", err) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/integration_tests/session_settimeout_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bunsenapp/go-selenium" 7 | ) 8 | 9 | func Test_SessionSetTimeout_CallingSetTimeoutWithoutSessionCausesAnError(t *testing.T) { 10 | setUp() 11 | defer tearDown() 12 | 13 | driver := createDriver(t) 14 | _, err := driver.SetSessionTimeout(goselenium.SessionPageLoadTimeout(20000)) 15 | if err == nil || !goselenium.IsSessionIDError(err) { 16 | errorAndWrap(t, "Session error was not returned.", err) 17 | } 18 | } 19 | 20 | func Test_SessionSetTimeout_CanSetScriptTimeout(t *testing.T) { 21 | setUp() 22 | defer tearDown() 23 | 24 | driver := createDriver(t) 25 | _, err := driver.CreateSession() 26 | if err != nil { 27 | errorAndWrap(t, "Error returned whilst creating session", err) 28 | } 29 | 30 | resp, err := driver.SetSessionTimeout(goselenium.SessionScriptTimeout(20000)) 31 | if err != nil || resp.State != "success" { 32 | errorAndWrap(t, "Error returned or state was not success whilst setting script timeout.", err) 33 | } 34 | 35 | printObjectResult(resp) 36 | } 37 | 38 | func Test_SessionSetTimeout_CanSetPageLoadTimeout(t *testing.T) { 39 | setUp() 40 | defer tearDown() 41 | 42 | driver := createDriver(t) 43 | _, err := driver.CreateSession() 44 | if err != nil { 45 | errorAndWrap(t, "Error returned whilst creating session", err) 46 | } 47 | 48 | resp, err := driver.SetSessionTimeout(goselenium.SessionPageLoadTimeout(20000)) 49 | if err != nil || resp.State != "success" { 50 | errorAndWrap(t, "Error returned or state was not success whilst setting page load timeout.", err) 51 | } 52 | 53 | printObjectResult(resp) 54 | } 55 | 56 | func Test_SessionSetTimeout_CanSetImplicitWaitTimeout(t *testing.T) { 57 | setUp() 58 | defer tearDown() 59 | 60 | driver := createDriver(t) 61 | _, err := driver.CreateSession() 62 | if err != nil { 63 | errorAndWrap(t, "Error returned whilst creating session", err) 64 | } 65 | 66 | resp, err := driver.SetSessionTimeout(goselenium.SessionImplicitWaitTimeout(20000)) 67 | if err != nil || resp.State != "success" { 68 | errorAndWrap(t, "Error returned or state was not success whilst setting implicit wait timeout.", err) 69 | } 70 | 71 | printObjectResult(resp) 72 | } 73 | -------------------------------------------------------------------------------- /test/integration_tests/session_status_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import "testing" 4 | 5 | func Test_SessionStatus_CanRetrieveStatusOfDriverSuccessfully(t *testing.T) { 6 | setUp() 7 | defer tearDown() 8 | 9 | driver := createDriver(t) 10 | resp, err := driver.SessionStatus() 11 | if err != nil || resp.State != "success" { 12 | errorAndWrap(t, "Error occurred or state is incorrect", err) 13 | } 14 | 15 | printObjectResult(resp) 16 | } 17 | -------------------------------------------------------------------------------- /test/integration_tests/test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "strconv" 8 | "testing" 9 | "time" 10 | 11 | "github.com/bunsenapp/go-selenium" 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | func setUp() { 16 | command := exec.Command("docker", "run", "-d", "--name=goselenium-tests", "-p=4444:4444", "selenium/standalone-firefox") 17 | command.CombinedOutput() 18 | time.Sleep(2000 * time.Millisecond) 19 | } 20 | 21 | func tearDown() { 22 | command := exec.Command("docker", "rm", "goselenium-tests", "-f") 23 | command.CombinedOutput() 24 | } 25 | 26 | func errorAndWrap(t *testing.T, message string, oldError error) { 27 | if oldError == nil { 28 | t.Errorf(errors.New(message).Error()) 29 | } else { 30 | err := errors.Wrap(oldError, message) 31 | t.Errorf(err.Error()) 32 | } 33 | } 34 | 35 | func printObjectResult(obj interface{}) { 36 | envResult := os.Getenv("GOSELENIUM_TEST_DETAIL") 37 | shouldShowDetailedResults, err := strconv.ParseBool(envResult) 38 | if shouldShowDetailedResults && err == nil { 39 | fmt.Println(fmt.Sprintf("Object returned: %+v", obj)) 40 | } 41 | } 42 | 43 | func createDriver(t *testing.T) goselenium.WebDriver { 44 | caps := goselenium.Capabilities{} 45 | caps.SetBrowser(goselenium.FirefoxBrowser()) 46 | 47 | driver, err := goselenium.NewSeleniumWebDriver("http://localhost:4444/wd/hub/", caps) 48 | if err != nil { 49 | t.Errorf("Driver creation threw an error.") 50 | } 51 | 52 | return driver 53 | } 54 | -------------------------------------------------------------------------------- /web_driver.go: -------------------------------------------------------------------------------- 1 | package goselenium 2 | 3 | import "time" 4 | 5 | // Keyboard keys converted from the ASCII code. 6 | const ( 7 | UnidentifiedKey = string('\uE000') 8 | CancelKey = string('\uE001') 9 | HelpKey = string('\uE002') 10 | BackspaceKey = string('\uE003') 11 | TabKey = string('\uE004') 12 | ClearKey = string('\uE005') 13 | ReturnKey = string('\uE006') 14 | EnterKey = string('\uE007') 15 | ShiftKey = string('\uE008') 16 | ControlKey = string('\uE009') 17 | AltKey = string('\uE00A') 18 | PauseKey = string('\uE00B') 19 | EscapeKey = string('\uE00C') 20 | SpaceKey = string('\uE00D') 21 | PageUpKey = string('\uE00E') 22 | PageDownKey = string('\uE00F') 23 | EndKey = string('\uE010') 24 | HomeKey = string('\uE011') 25 | ArrowLeftKey = string('\uE012') 26 | ArrowUpKey = string('\uE013') 27 | ArrowRightKey = string('\uE014') 28 | ArrowDownKey = string('\uE015') 29 | InsertKey = string('\uE016') 30 | DeleteKey = string('\uE017') 31 | SemiColonKey = string('\uE018') 32 | EqualsKey = string('\uE019') 33 | AsteriskKey = string('\uE024') 34 | PlusKey = string('\uE025') 35 | CommaKey = string('\uE026') 36 | MinusKey = string('\uE027') 37 | PeriodKey = string('\uE028') 38 | ForwardSlashKey = string('\uE029') 39 | F1Key = string('\uE031') 40 | F2Key = string('\uE032') 41 | F3Key = string('\uE033') 42 | F4Key = string('\uE034') 43 | F5Key = string('\uE035') 44 | F6Key = string('\uE036') 45 | F7Key = string('\uE037') 46 | F8Key = string('\uE038') 47 | F9Key = string('\uE039') 48 | F10Key = string('\uE03A') 49 | F11Key = string('\uE03B') 50 | F12Key = string('\uE03C') 51 | MetaKey = string('\uE03D') 52 | ZenkakuHankakuKey = string('\uE040') 53 | ) 54 | 55 | // Error codes that are returned from Selenium. Infer that the 56 | // type of the error returned is CommunicationError, then do a comparison 57 | // on the err.Response.State field to one of the below constants. 58 | const ( 59 | ElementNotSelectable = "element not selectable" 60 | ElementNotInteractable = "element not interactable" 61 | InsecureCertificate = "insecure certificate" 62 | InvalidArgument = "invalid argument" 63 | InvalidCookieDomain = "invalid cookie domain" 64 | InvalidCoordinates = "invalid coordinates" 65 | InvalidElementState = "invalid element state" 66 | InvalidSelector = "invalid selector" 67 | InvalidSessionID = "invalid session id" 68 | JavascriptError = "javascript error" 69 | MoveTargetOutOfBounds = "move target out of bounds" 70 | NoSuchAlert = "no such alert" 71 | NoSuchCookie = "no such cookie" 72 | NoSuchElement = "no such element" 73 | NoSuchFrame = "no such frame" 74 | NoSuchWindow = "no such window" 75 | ScriptTimeout = "script timeout" 76 | SessionNotCreated = "session not created" 77 | StaleElementReference = "stale element reference" 78 | TimeoutError = "timeout" 79 | UnableToSetCookie = "unable to set cookie" 80 | UnableToCaptureScreen = "unable to capture screen" 81 | UnexpectedAlertOpen = "unexpected alert open" 82 | UnknownCommand = "unknown command" 83 | UnknownError = "unknown error" 84 | UnknownMethod = "unknown method" 85 | UnsupportedOperation = "unsupported operation" 86 | ) 87 | 88 | // WebDriver is an interface which adheres to the W3C specification 89 | // for WebDrivers (https://w3c.github.io/webdriver/webdriver-spec.html). 90 | type WebDriver interface { 91 | /* 92 | PROPERTY ACCESS METHODS 93 | */ 94 | 95 | // DriverURL returns the URL where the W3C compliant web driver is hosted. 96 | DriverURL() string 97 | 98 | /* 99 | SESSION METHODS 100 | */ 101 | 102 | // CreateSession creates a session in the remote driver with the 103 | // desired capabilities. 104 | CreateSession() (*CreateSessionResponse, error) 105 | 106 | // DeleteSession deletes the current session associated with the web driver. 107 | DeleteSession() (*DeleteSessionResponse, error) 108 | 109 | // SessionStatus gets the status about whether a remove end is in a state 110 | // which it can create new sessions. 111 | SessionStatus() (*SessionStatusResponse, error) 112 | 113 | // SetSessionTimeout sets a timeout for one of the 3 options. 114 | // Call SessionScriptTimeout() to generate a script timeout. 115 | // Call SessionPageLoadTimeout() to generate a page load timeout. 116 | // Call SessionImplicitWaitTimeout() to generate an implicit wait timeout. 117 | SetSessionTimeout(to Timeout) (*SetSessionTimeoutResponse, error) 118 | 119 | /* 120 | NAVIGATION METHODS 121 | */ 122 | 123 | // Go forces the browser to perform a GET request on a URL. 124 | Go(url string) (*GoResponse, error) 125 | 126 | // CurrentURL returns the current URL of the top level browsing context. 127 | CurrentURL() (*CurrentURLResponse, error) 128 | 129 | // Back instructs the web driver to go one step back in the page history. 130 | Back() (*BackResponse, error) 131 | 132 | // Forward instructs the web driver to go one step forward in the page history. 133 | Forward() (*ForwardResponse, error) 134 | 135 | // Refresh instructs the web driver to refresh the page that it is currently on. 136 | Refresh() (*RefreshResponse, error) 137 | 138 | // Title gets the title of the current page of the web driver. 139 | Title() (*TitleResponse, error) 140 | 141 | /* 142 | COMMAND METHODS 143 | */ 144 | 145 | // WindowHandle retrieves the current active browsing string for the current session. 146 | WindowHandle() (*WindowHandleResponse, error) 147 | 148 | // CloseWindow closes the current active window (see WindowHandle() for what 149 | // window that will be). 150 | CloseWindow() (*CloseWindowResponse, error) 151 | 152 | // SwitchToWindow switches the current browsing context to a specified window 153 | // handle. 154 | SwitchToWindow(handle string) (*SwitchToWindowResponse, error) 155 | 156 | // WindowHandles gets all of the window handles for the current session. 157 | // To retrieve the currently active window handle, see WindowHandle(). 158 | WindowHandles() (*WindowHandlesResponse, error) 159 | 160 | // SwitchToFrame switches to a frame determined by the "by" parameter. 161 | // You can use ByIndex to find the frame to switch to. Any other 162 | // By implementation will yield an InvalidByParameter error. 163 | SwitchToFrame(by By) (*SwitchToFrameResponse, error) 164 | 165 | // SwitchToParentFrame switches to the parent of the current top level 166 | // browsing context. 167 | SwitchToParentFrame() (*SwitchToParentFrameResponse, error) 168 | 169 | // WindowSize retrieves the current browser window size for the 170 | // active session. 171 | WindowSize() (*WindowSizeResponse, error) 172 | 173 | // SetWindowSize sets the current browser window size for the active 174 | // session. 175 | SetWindowSize(dimensions *Dimensions) (*SetWindowSizeResponse, error) 176 | 177 | // Maximize increases the current browser window to its maximum size. 178 | MaximizeWindow() (*MaximizeWindowResponse, error) 179 | 180 | /* 181 | ELEMENT METHODS 182 | */ 183 | 184 | // FindElement finds an element via a By implementation (i.e. CSS selector, 185 | // x-path). Attempting to find via index will result in an argument error 186 | // being thrown. 187 | FindElement(by By) (Element, error) 188 | 189 | // FindElements works the same way as FindElement but can return more than 190 | // one result. 191 | FindElements(by By) ([]Element, error) 192 | 193 | /* 194 | DOCUMENT HANDLING METHODS 195 | */ 196 | 197 | // PageSource retrieves the outerHTML value of the current URL. 198 | PageSource() (*PageSourceResponse, error) 199 | 200 | // ExecuteScript executes a Javascript script on the currently active 201 | // page. 202 | ExecuteScript(script string) (*ExecuteScriptResponse, error) 203 | 204 | // ExecuteScriptAsync executes a Javascript script asynchronously on the 205 | // currently active page. If you do not have experience with this call, 206 | // there is an example below. 207 | // 208 | // The async handler runs on the concept of a callback; meaning it will run 209 | // your code asynchronously and if it completes, will call the callback. 210 | // 211 | // Selenium helpfully provides a callback function which is passed in 212 | // to the 'arguments' array that you can access within your script. The 213 | // callback function is always the LAST element of the array. You can 214 | // access it like the below: 215 | // var callback = arguments[arguments.length - 1]; 216 | // The callback function also accepts one argument as a parameter, this 217 | // can be anything and will be assigned to the Response property of 218 | // ExecuteScriptResponse. 219 | // 220 | // An example: 221 | // var callback = arguments[arguments.length - 1]; 222 | // doLongWindedTask(); 223 | // callback(); 224 | ExecuteScriptAsync(script string) (*ExecuteScriptResponse, error) 225 | 226 | /* 227 | COOKIE METHODS 228 | */ 229 | 230 | // AllCookies returns all cookies associated with the active URL of the 231 | // current browsing context. 232 | AllCookies() (*AllCookiesResponse, error) 233 | 234 | // Cookie gets a single named cookie associated with the active URL of the 235 | // current browsing context. 236 | Cookie(name string) (*CookieResponse, error) 237 | 238 | // AddCookie adds a cookie to the current browsing context. 239 | AddCookie(c *Cookie) (*AddCookieResponse, error) 240 | 241 | // DeleteCookie deletes a cookie from the current browsing session. If name 242 | // is passed as an empty string, all cookies for the current address will 243 | // be deleted. 244 | DeleteCookie(name string) (*DeleteCookieResponse, error) 245 | 246 | /* 247 | ALERT METHODS 248 | */ 249 | 250 | // DismissAlert dismisses an alert if there is one present. If not, it will 251 | // throw an error. 252 | DismissAlert() (*DismissAlertResponse, error) 253 | 254 | // AcceptAlertResponse accepts an alert if there is one present. If not, 255 | // it will throw an error. 256 | AcceptAlert() (*AcceptAlertResponse, error) 257 | 258 | // AlertText gets the text associated with the current alert. If there is 259 | // not an alert, it will throw an error. 260 | AlertText() (*AlertTextResponse, error) 261 | 262 | // SendAlertText enters the text specified into the value box. 263 | // 264 | // If the prompt is of type 'alert' or 'confirm', a communication error 265 | // with code 'element not interactable' will be returned. 266 | // 267 | // If the prompt is anything other than 'prompt', a communication error with 268 | // code 'unsupported operation' will be returned. 269 | SendAlertText(text string) (*SendAlertTextResponse, error) 270 | 271 | /* 272 | SCREEN CAPTURE METHODS 273 | */ 274 | 275 | // Screenshot takes a screenshot of the window of the current top level 276 | // browsing context. The image returned will be a PNG image. 277 | Screenshot() (*ScreenshotResponse, error) 278 | 279 | /* 280 | HELPER METHODS 281 | */ 282 | 283 | // Wait repeats an action until the action yields a valid result or 284 | // until the timeout is reached. If the timeout is reached without 285 | // the until function returning a satisfactory result, this method 286 | // will return false. 287 | // 288 | // time.Sleep will be called with the sleep parameter after every 289 | // u iteration. 290 | Wait(u Until, timeout time.Duration, sleep time.Duration) bool 291 | } 292 | 293 | // Element is an interface which specifies what all WebDriver elements 294 | // must do. Any requests which involve this element (i.e. FindElements that 295 | // are children of the current element) will be specified. Anything not related 296 | // will exist within the WebDriver interface/implementation. 297 | type Element interface { 298 | // ID is the assigned ID for the element returned from the Selenium driver. 299 | ID() string 300 | 301 | // Selected gets whether or not the current element is selected. This only 302 | // makes sense for inputs such as radio buttons and checkboxes. 303 | Selected() (*ElementSelectedResponse, error) 304 | 305 | // Attribute retrieves an attribute (i.e. href, class) of the current 306 | // active element. 307 | Attribute(att string) (*ElementAttributeResponse, error) 308 | 309 | // CSSValue retrieves a CSS property associated with the current element. 310 | // As an example, this could be the 'background' or 'font-family' properties. 311 | CSSValue(prop string) (*ElementCSSValueResponse, error) 312 | 313 | // Text gets the value of element.innerText for the current element. 314 | Text() (*ElementTextResponse, error) 315 | 316 | // TagName gets the HTML element name (i.e. p, div) of the currently selected 317 | // element. 318 | TagName() (*ElementTagNameResponse, error) 319 | 320 | // Rectangle gets the dimensions and co-ordinates of the currently selected 321 | // element. 322 | Rectangle() (*ElementRectangleResponse, error) 323 | 324 | // Enabled gets whether or not the current selected elemented is enabled. 325 | Enabled() (*ElementEnabledResponse, error) 326 | 327 | // Click clicks the currently selected element. Please note, you may have to 328 | // implement your own wait to ensure the page actually navigates. This is due to 329 | // Selenium having no idea whether or not your click will be interrupted by JS. 330 | // Alternatively, you can use the WaitUntil(TitleEquals("title"), 20) to 331 | // automatically wait until the page title has changed. 332 | Click() (*ElementClickResponse, error) 333 | 334 | // Clear clears the currently selected element according to the specification. 335 | Clear() (*ElementClearResponse, error) 336 | 337 | // SendKeys sends a set of keystrokes to the currently selected element. 338 | SendKeys(keys string) (*ElementSendKeysResponse, error) 339 | } 340 | 341 | // Timeout is an interface which specifies what all timeout requests must follow. 342 | type Timeout interface { 343 | // Type is the type of the timeout that is being used. 344 | Type() string 345 | 346 | // Timeout is the timeout in milliseconds. 347 | Timeout() int 348 | } 349 | 350 | // By is an interface that defines what all 'ByX' methods must return. 351 | type By interface { 352 | // Type is the type of by (i.e. id, xpath, class, name, index). 353 | Type() string 354 | 355 | // Value is the actual value to retrieve something by (i.e. #test, 1). 356 | Value() interface{} 357 | } 358 | --------------------------------------------------------------------------------