Subclass for Geckodriver
This commit is contained in:
parent
b6209258c1
commit
8f1bdccae7
|
@ -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
|
||||||
]
|
]
|
||||||
|
|
|
@ -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.
|
||||||
|
]
|
Loading…
Reference in New Issue