Subclass for Geckodriver

This commit is contained in:
Daniel Ziltener 2022-04-27 02:48:41 +02:00
parent b6209258c1
commit 8f1bdccae7
2 changed files with 154 additions and 65 deletions

View File

@ -1,3 +1,34 @@
"
I am a cleanroom implementation of the W3C WebDriver protocol.
Please comment me using the following template inspired by Class Responsibility Collaborator (CRC) design:
For the Class part: State a one line summary. For example, ""I represent a paragraph of text"".
For the Responsibility part: Three sentences about my main responsibilities - what I do, what I know.
For the Collaborators Part: State my main collaborators and one line about how I interact with them.
Public API and Key Messages
- message one
- message two
- (for bonus points) how to create instances.
One simple example is simply gorgeous.
Internal Representation and Key Implementation Points.
Instance Variables
browser: <Object>
optionsName: <Object>
port: <Object>
server: <Object>
sessionId: <Object>
Implementation Points
"
Class { Class {
#name : #WebDriver, #name : #WebDriver,
#superclass : #Object, #superclass : #Object,
@ -6,7 +37,6 @@ Class {
'server', 'server',
'port', 'port',
'sessionId', 'sessionId',
'parameters',
'optionsName' 'optionsName'
], ],
#category : #WebDriver #category : #WebDriver
@ -18,7 +48,7 @@ WebDriver class >> geckodriver [
subproc := OSSUnixSubprocess new subproc := OSSUnixSubprocess new
command: 'geckodriver'. command: 'geckodriver'.
[ subproc run ] fork. [ subproc run ] fork.
^ self new ^ WebDriverGeckodriver new
browser: subproc browser: subproc
server: '127.0.0.1' server: '127.0.0.1'
port: 4444 port: 4444
@ -27,113 +57,130 @@ WebDriver class >> geckodriver [
{ #category : #accessing } { #category : #accessing }
WebDriver >> attribute: attr from: element [ WebDriver >> attribute: attr from: element [
^ (self send: { }
to: 'session/',sessionId,'/element/',element,'/attribute/',attr ^ self
using: #GET) send: { }
at: #value. to:
'session/' , sessionId , '/element/' , element , '/attribute/'
, attr
using: #GET
] ]
{ #category : #navigation } { #category : #navigation }
WebDriver >> back [ WebDriver >> back [
self send: { } to: 'session/',sessionId,'/back' using: #POST.
]
{ #category : #initialization } self send: { } to: 'session/' , sessionId , '/back' using: #POST
WebDriver >> browser: brs server: srv port: portnum [
browser := brs.
server := srv.
port := portnum.
] ]
{ #category : #initialization } { #category : #initialization }
WebDriver >> browser: brs server: srv port: portnum optionsName: optName [ WebDriver >> browser: brs server: srv port: portnum optionsName: optName [
browser := brs. browser := brs.
server := srv. server := srv.
port := portnum. port := portnum.
optionsName := optName. optionsName := optName
]
{ #category : #'event handling' }
WebDriver >> click: elem [
self
send: { }
to: 'session/' , sessionId , '/element/' , elem , '/click'
using: #POST
]
{ #category : #'private - utilities' }
WebDriver >> constructCapabilities: caps [
self subclassResponsibility
] ]
{ #category : #finalization } { #category : #finalization }
WebDriver >> deleteSession [ WebDriver >> deleteSession [
"Deletes the session." "Deletes the session."
self send: {} to: 'session/',sessionId using: #DELETE.
self send: { } to: 'session/' , sessionId using: #DELETE
] ]
{ #category : #finalization } { #category : #finalization }
WebDriver >> finalize [ WebDriver >> finalize [
browser terminate. browser terminate.
super finalize. super finalize
] ]
{ #category : #accessing } { #category : #accessing }
WebDriver >> findElement: elemSelector using: locStrategy [ WebDriver >> findElement: elemSelector using: locStrategy [
| reply | | reply |
reply := (self send: { #using -> locStrategy. #value -> elemSelector. } reply := self
to: 'session/',sessionId,'/element' send: {
using: #POST) (#using -> locStrategy).
at: #value. (#value -> elemSelector) }
to: 'session/' , sessionId , '/element'
using: #POST.
(reply at: #error ifPresent: [ true ] ifAbsent: [ false ]) (reply at: #error ifPresent: [ true ] ifAbsent: [ false ])
ifTrue: [ ^ WDException raise: reply ] ifTrue: [ ^ WDException raise: reply ]
ifFalse: [ ^ (WDElement fromString: reply values first) ] ifFalse: [ ^ WDElement fromString: reply values first ]
] ]
{ #category : #accessing } { #category : #accessing }
WebDriver >> findElements: elemSelector using: locStrategy [ WebDriver >> findElements: elemSelector using: locStrategy [
| reply | | reply |
reply := (self send: { #using -> locStrategy. #value -> elemSelector. } reply := self
to: 'session/',sessionId,'/elements' send: {
using: #POST) (#using -> locStrategy).
at: #value. (#value -> elemSelector) }
(reply isArray) to: 'session/' , sessionId , '/elements'
ifTrue: [ ^ reply collect: [ :elem | WDElement fromString: elem values first ] ] using: #POST.
reply isArray
ifTrue: [
^ reply collect: [ :elem | WDElement fromString: elem values first ] ]
ifFalse: [ ^ WDException raise: reply ] ifFalse: [ ^ WDException raise: reply ]
] ]
{ #category : #navigation } { #category : #navigation }
WebDriver >> forward [ WebDriver >> forward [
self send: { } to: 'session/',sessionId,'/forward' using: #POST.
self send: { } to: 'session/' , sessionId , '/forward' using: #POST
] ]
{ #category : #accessing } { #category : #accessing }
WebDriver >> property: attr from: element [ WebDriver >> property: attr from: element [
^ (self ^ self
send: { } send: { }
to: to:
'session/' , sessionId , '/element/' , element , '/property/' 'session/' , sessionId , '/element/' , element , '/property/'
, attr , attr
using: #GET) at: #value using: #GET
] ]
{ #category : #navigation } { #category : #navigation }
WebDriver >> refresh [ WebDriver >> refresh [
self send: { } to: 'session/',sessionId,'/refresh' using: #POST.
self send: { } to: 'session/' , sessionId , '/refresh' using: #POST
] ]
{ #category : #navigation } { #category : #navigation }
WebDriver >> send: dict to: url using: method [ WebDriver >> send: dict to: url using: method [
|result| ^ self subclassResponsibility.
result := nil.
ZnClient new
host: server;
port: port;
path: url;
contents: (NeoJSONWriter toString: dict asDictionary);
contentType: ZnMimeType applicationJson;
method: method;
contentReader: [ :entity |
result := (NeoJSONReader on: (entity contents) readStream) propertyNamesAsSymbols: true; next.
];
execute.
^result.
] ]
{ #category : #accessing } { #category : #accessing }
WebDriver >> session [ WebDriver >> session [
"Initializes a new WebDriver session." "Initializes a new WebDriver session."
sessionId
ifNil: [ sessionId := (self send: {} to: 'session' using: #POST) at: #value at: #sessionId ]. sessionId ifNil: [
^sessionId. sessionId := (self
send: (self constructCapabilities: { })
to: 'session'
using: #POST) at: #sessionId ].
^ sessionId
] ]
{ #category : #accessing } { #category : #accessing }
@ -141,9 +188,9 @@ WebDriver >> session: capabilities [
sessionId ifNotNil: [ ^ self session ] ifNil: [ sessionId ifNotNil: [ ^ self session ] ifNil: [
sessionId := (self sessionId := (self
send: { #desiredCapabilities -> ({ optionsName -> ({#prefs -> capabilities}) asDictionary } asDictionary) } send: (self constructCapabilities: capabilities)
to: 'session' to: 'session'
using: #POST) at: #value at: #sessionId. using: #POST) at: #sessionId.
^ sessionId ] ^ sessionId ]
] ]
@ -154,36 +201,43 @@ WebDriver >> source [
send: { } send: { }
to: to:
'session/' , sessionId , '/source' 'session/' , sessionId , '/source'
using: #GET) at: #value using: #GET)
] ]
{ #category : #accessing } { #category : #accessing }
WebDriver >> status [ WebDriver >> status [
^ (self send: { } to: 'status' using: #GET) at: #value.
^ self send: { } to: 'status' using: #GET
] ]
{ #category : #accessing } { #category : #accessing }
WebDriver >> title [ WebDriver >> title [
^ (self send: { } to: 'session/',sessionId,'/title' using: #GET) at: #value.
^ self send: { } to: 'session/' , sessionId , '/title' using: #GET
] ]
{ #category : #'text input' } { #category : #'text input' }
WebDriver >> type: text into: element [ WebDriver >> type: text into: element [
self send: { #text -> text } to: 'session/',sessionId,'/element/',element,'/value' using: #POST.
self
send: { (#text -> text) }
to: 'session/' , sessionId , '/element/' , element , '/value'
using: #POST
] ]
{ #category : #accessing } { #category : #accessing }
WebDriver >> url [ WebDriver >> url [
^ (self ^ self send: { } to: 'session/' , sessionId , '/url' using: #GET
send: { }
to:
'session/' , sessionId , '/url'
using: #GET) at: #value
] ]
{ #category : #navigation } { #category : #navigation }
WebDriver >> url: url [ WebDriver >> url: url [
"Navigates the browser to the given URL." "Navigates the browser to the given URL."
self send: { #url -> url } to: 'session/',sessionId,'/url' using: #POST.
self
send: { (#url -> url) }
to: 'session/' , sessionId , '/url'
using: #POST
] ]

View File

@ -0,0 +1,35 @@
"
I implement the specifics and quirks to remote control a Firefox instance using Geckodriver.
"
Class {
#name : #WebDriverGeckodriver,
#superclass : #WebDriver,
#category : #WebDriver
}
{ #category : #'private - utilities' }
WebDriverGeckodriver >> constructCapabilities: caps [
^ { (#desiredCapabilities
->
{ (optionsName -> { (#prefs -> caps) } asDictionary) }
asDictionary) }
]
{ #category : #navigation }
WebDriverGeckodriver >> send: dict to: url using: method [
|result|
result := nil.
ZnClient new
host: server;
port: port;
path: url;
contents: (NeoJSONWriter toString: dict asDictionary);
contentType: ZnMimeType applicationJson;
method: method;
contentReader: [ :entity |
result := (NeoJSONReader on: (entity contents) readStream) propertyNamesAsSymbols: true; next.
];
execute.
^result at: #value.
]