diff --git a/src/WebDriver-Tests/WebDriverTest.class.st b/src/WebDriver-Tests/WebDriverTest.class.st index e44fb1a..a178f5d 100644 --- a/src/WebDriver-Tests/WebDriverTest.class.st +++ b/src/WebDriver-Tests/WebDriverTest.class.st @@ -23,6 +23,18 @@ WebDriverTest >> tearDown [ super tearDown. ] +{ #category : #tests } +WebDriverTest >> testAttributeValue [ + + | element | + element := webdriver + url: 'http://info.cern.ch'; + findElement: 'a' using: WDLocationStrategy cssSelector. + self + assert: (webdriver attribute: 'href' from: element) + equals: 'http://info.cern.ch/hypertext/WWW/TheProject.html' +] + { #category : #tests } WebDriverTest >> testFindElementInvalidCSSSelector [ self @@ -51,3 +63,8 @@ WebDriverTest >> testFindElementsValid [ findElements: '.badge-link__bullet' using: WDLocationStrategy cssSelector. self assert: result size equals: 3. ] + +{ #category : #tests } +WebDriverTest >> testGetTitle [ + self assert: (webdriver url: 'https://cern.ch'; title) equals: 'Home | CERN' +] diff --git a/src/WebDriver/WDElement.class.st b/src/WebDriver/WDElement.class.st new file mode 100644 index 0000000..1c637ab --- /dev/null +++ b/src/WebDriver/WDElement.class.st @@ -0,0 +1,33 @@ +Class { + #name : #WDElement, + #superclass : #String, + #instVars : [ + 'selector', + 'locationStrategy' + ], + #category : #WebDriver +} + +{ #category : #accessing } +WDElement >> locationStrategy [ + + ^ locationStrategy +] + +{ #category : #accessing } +WDElement >> locationStrategy: anObject [ + + locationStrategy := anObject +] + +{ #category : #accessing } +WDElement >> selector [ + + ^ selector +] + +{ #category : #accessing } +WDElement >> selector: anObject [ + + selector := anObject +] diff --git a/src/WebDriver/WDElementClickIntercepted.class.st b/src/WebDriver/WDElementClickIntercepted.class.st new file mode 100644 index 0000000..007dfae --- /dev/null +++ b/src/WebDriver/WDElementClickIntercepted.class.st @@ -0,0 +1,8 @@ +" +The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked. +" +Class { + #name : #WDElementClickIntercepted, + #superclass : #WDException, + #category : #WebDriver +} diff --git a/src/WebDriver/WDElementNotInteractable.class.st b/src/WebDriver/WDElementNotInteractable.class.st new file mode 100644 index 0000000..a6d5242 --- /dev/null +++ b/src/WebDriver/WDElementNotInteractable.class.st @@ -0,0 +1,8 @@ +" +A command could not be completed because the element is not pointer- or keyboard interactable. +" +Class { + #name : #WDElementNotInteractable, + #superclass : #WDException, + #category : #WebDriver +} diff --git a/src/WebDriver/WDException.class.st b/src/WebDriver/WDException.class.st new file mode 100644 index 0000000..5d7e526 --- /dev/null +++ b/src/WebDriver/WDException.class.st @@ -0,0 +1,55 @@ +" +I am the parent class of all WebDriver exceptions. +" +Class { + #name : #WDException, + #superclass : #Exception, + #instVars : [ + 'stacktrace', + 'data' + ], + #category : #WebDriver +} + +{ #category : #'as yet unclassified' } +WDException class >> raise: errDict [ + "Initializes and populates the appropriate exception class." + | error message stacktrace data | + error := errDict at: #error. + message := errDict at: #message. + stacktrace := errDict at: #stacktrace. + data := errDict at: #data ifAbsent: nil. + (error = 'element click intercepted') + ifTrue: [ ^ WDElementClickIntercepted new data: data; stacktrace: stacktrace; signal: message ]. + (error = 'element not interactable') + ifTrue: [ ^ WDElementNotInteractable new data: data; stacktrace: stacktrace; signal: message ]. + (error = 'insecure certificate') + ifTrue: [ ^ WDInsecureCertificate new data: data; stacktrace: stacktrace; signal: message ]. + (error = 'invalid selector') + ifTrue: [ ^ WDInvalidSelector new data: data; stacktrace: stacktrace; signal: message ]. + +] + +{ #category : #accessing } +WDException >> data [ + + ^ data +] + +{ #category : #accessing } +WDException >> data: anObject [ + + data := anObject +] + +{ #category : #accessing } +WDException >> stacktrace [ + + ^ stacktrace +] + +{ #category : #accessing } +WDException >> stacktrace: anObject [ + + stacktrace := anObject +] diff --git a/src/WebDriver/WDInsecureCertificate.class.st b/src/WebDriver/WDInsecureCertificate.class.st new file mode 100644 index 0000000..e7dd32c --- /dev/null +++ b/src/WebDriver/WDInsecureCertificate.class.st @@ -0,0 +1,5 @@ +Class { + #name : #WDInsecureCertificate, + #superclass : #WDException, + #category : #WebDriver +} diff --git a/src/WebDriver/WDInvalidSelector.class.st b/src/WebDriver/WDInvalidSelector.class.st index b4a762b..2850703 100644 --- a/src/WebDriver/WDInvalidSelector.class.st +++ b/src/WebDriver/WDInvalidSelector.class.st @@ -1,5 +1,8 @@ +" +Argument was an invalid selector. +" Class { #name : #WDInvalidSelector, - #superclass : #Exception, + #superclass : #WDException, #category : #WebDriver } diff --git a/src/WebDriver/WDKeys.class.st b/src/WebDriver/WDKeys.class.st index 8ec1efa..1aab8f8 100644 --- a/src/WebDriver/WDKeys.class.st +++ b/src/WebDriver/WDKeys.class.st @@ -1,3 +1,6 @@ +" +I provide class methods returning character codes for non-printable characters. +" Class { #name : #WDKeys, #superclass : #Object, diff --git a/src/WebDriver/WDLocationStrategy.class.st b/src/WebDriver/WDLocationStrategy.class.st index cb2b589..a72436b 100644 --- a/src/WebDriver/WDLocationStrategy.class.st +++ b/src/WebDriver/WDLocationStrategy.class.st @@ -1,3 +1,6 @@ +" +I provide location strategy values used in messages like `WebDriver>>findElement:using:`. +" Class { #name : #WDLocationStrategy, #superclass : #Object, diff --git a/src/WebDriver/WebDriver.class.st b/src/WebDriver/WebDriver.class.st index 2501731..f8014b9 100644 --- a/src/WebDriver/WebDriver.class.st +++ b/src/WebDriver/WebDriver.class.st @@ -23,6 +23,15 @@ WebDriver class >> geckodriver [ port: 4444. ] +{ #category : #accessing } +WebDriver >> attribute: attr from: element [ + ^ (self send: { } + to: 'session/',sessionId,'/element/',element,'/attribute/',attr + using: #GET) + at: #value. + +] + { #category : #navigation } WebDriver >> back [ self send: { } to: 'session/',sessionId,'/back' using: #POST. @@ -55,8 +64,8 @@ WebDriver >> findElement: elemSelector using: locStrategy [ using: #POST) at: #value. (reply at: #error ifPresent: [ true ] ifAbsent: [ false ]) - ifTrue: [ ^ WDInvalidSelector new signal: (reply at: #message) ] - ifFalse: [ ^ reply values first ] + ifTrue: [ ^ WDException raise: reply ] + ifFalse: [ ^ (WDElement fromString: reply values first) ] ] { #category : #accessing } @@ -67,8 +76,8 @@ WebDriver >> findElements: elemSelector using: locStrategy [ using: #POST) at: #value. (reply isArray) - ifTrue: [ ^ reply collect: [ :elem | elem values first ] ] - ifFalse: [ ^ WDInvalidSelector new signal: (reply at: #message) ] + ifTrue: [ ^ reply collect: [ :elem | WDElement fromString: elem values first ] ] + ifFalse: [ ^ WDException raise: reply ] ] { #category : #navigation } @@ -76,12 +85,6 @@ WebDriver >> forward [ self send: { } to: 'session/',sessionId,'/forward' using: #POST. ] -{ #category : #initialization } -WebDriver >> initialize: srv port: portnum [ - server := srv. - port := portnum. -] - { #category : #navigation } WebDriver >> refresh [ self send: { } to: 'session/',sessionId,'/refresh' using: #POST. @@ -100,6 +103,8 @@ WebDriver >> send: dict to: url using: method [ method: method; contentReader: [ :entity | result := (NeoJSONReader on: (entity contents) readStream) propertyNamesAsSymbols: true; next. + "(result at: #error ifPresent: [ true ] ifAbsent: [ false ]) + ifTrue: [ result := WDException raise: result ]" ]; execute. ^result. @@ -108,7 +113,8 @@ WebDriver >> send: dict to: url using: method [ { #category : #accessing } WebDriver >> session [ "Initializes a new WebDriver session." - sessionId := (self send: {} to: 'session' using: #POST) at: #value at: #sessionId. + sessionId + ifNil: [ sessionId := (self send: {} to: 'session' using: #POST) at: #value at: #sessionId ]. ^sessionId. ] @@ -125,7 +131,6 @@ WebDriver >> title [ { #category : #'text input' } WebDriver >> type: text into: element [ self send: { #text -> text } to: 'session/',sessionId,'/element/',element,'/value' using: #POST. - ] { #category : #navigation }