WebDriver/src/WebDriver/WebDriver.class.st

347 lines
8.3 KiB
Smalltalk

"
I am a cleanroom implementation of the [W3C WebDriver protocol](https://w3c.github.io/webdriver/).
I am responsible for dispatching the correct messages to start remotely controlled browsers and send commands to them.
My subclasses implement browser-specific behaviour.
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 {
#name : #WebDriver,
#superclass : #Object,
#instVars : [
'browser',
'server',
'port',
'sessionId',
'capabilities'
],
#category : #'WebDriver-Base'
}
{ #category : #testing }
WebDriver class >> matchesBrowserType: browserType [
^ self subclassResponsibility.
]
{ #category : #accessing }
WebDriver class >> start: browserType [
| emptyOpts |
emptyOpts := Dictionary new.
^ self start: browserType with: emptyOpts.
]
{ #category : #'instance creation' }
WebDriver class >> start: browserType with: options [
"comment stating purpose of class-side method"
"scope: class-variables & class-instance-variables"
| matchingClass |
matchingClass := self subclasses
detect: [ :operationClass | operationClass matchesBrowserType: browserType ]
ifNone: [ self ].
^ matchingClass startWithOptions: options.
]
{ #category : #'instance creation' }
WebDriver class >> startWithOptions: options [
^ self subclassResponsibility.
]
{ #category : #navigation }
WebDriver >> back [
self send: { } to: 'session/' , sessionId , '/back' using: #POST
]
{ #category : #initialization }
WebDriver >> browser: brs server: srv port: portnum [
browser := brs.
server := srv.
port := portnum.
]
{ #category : #'private - utilities' }
WebDriver >> browserDetails [
^ self subclassResponsibility
]
{ #category : #'private - utilities' }
WebDriver >> constructCapabilities: caps [
self subclassResponsibility
]
{ #category : #finalization }
WebDriver >> deleteSession [
"Deletes the session."
self send: { } to: 'session/' , sessionId using: #DELETE
]
{ #category : #simulating }
WebDriver >> executeAsync: script with: args [
^ self sendWithSession: { #script -> script . #args -> args }
to: 'execute/async'
using: #POST.
]
{ #category : #simulating }
WebDriver >> executeSync: script with: args [
^ self sendWithSession: { #script -> script . #args -> args }
to: 'execute/sync'
using: #POST.
]
{ #category : #finalization }
WebDriver >> finalize [
browser terminate.
super finalize
]
{ #category : #accessing }
WebDriver >> findElement: elemSelector using: locStrategy [
| reply |
reply := self
send: {
(#using -> locStrategy).
(#value -> elemSelector) }
to: 'session/' , sessionId , '/element'
using: #POST.
^ WDElement new
driver: self;
element: reply values first.
]
{ #category : #accessing }
WebDriver >> findElements: elemSelector using: locStrategy [
| reply |
reply := self
send: {
(#using -> locStrategy).
(#value -> elemSelector) }
to: 'session/' , sessionId , '/elements'
using: #POST.
^ reply collect: [ :elem | WDElement new driver: self; element: elem values first. ]
]
{ #category : #navigation }
WebDriver >> forward [
self send: { } to: 'session/' , sessionId , '/forward' using: #POST
]
{ #category : #'private - utilities' }
WebDriver >> inspectionWebDriver [
<inspectorPresentationOrder: 10 title: 'Browser'>
| presenter browserInfo |
browserInfo := self browserDetails.
presenter := SpBoxLayout newTopToBottom
add: (SpLabelPresenter new
label: 'Connection Infos';
yourself)
expand: false
fill: false
padding: 5;
add: (SpTablePresenter new
addColumn: (SpStringTableColumn
title: 'Key'
evaluated: [ :anObject | anObject at: #title ]);
addColumn: (SpStringTableColumn
title: 'Value'
evaluated: [ :anObject | anObject at: #text ]);
items: {
(Dictionary new
at: #title put: 'Browser';
at: #text put: (browserInfo at: #name);
yourself).
(Dictionary new
at: #title put: 'Driver';
at: #text put: (browserInfo at: #driver);
yourself).
(Dictionary new
at: #title put: 'Address';
at: #text put: ('http://{1}:{2}/' format: {
server.
port });
yourself) })
expand: true
fill: true
padding: 5;
add:
(SpLabelPresenter new label: 'Session Capabilities')
expand: false
fill: false
padding: 5;
add: capabilities asStringTreeTablePresenter
expand: true
fill: true
padding: 5 yourself.
presenter := SpPresenter new
layout: presenter;
yourself.
^ presenter
]
{ #category : #'private - utilities' }
WebDriver >> postprocessResult: result [
^ result.
]
{ #category : #utilities }
WebDriver >> print [
^ self sendWithSession: { } to: 'print' using: #POST.
]
{ #category : #navigation }
WebDriver >> refresh [
self send: { } to: 'session/' , sessionId , '/refresh' using: #POST
]
{ #category : #accessing }
WebDriver >> screenshot [
^ (self sendWithSession: '' to: 'screenshot' using: #GET) base64Decoded.
]
{ #category : #'private - utilities' }
WebDriver >> 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.
result := self postprocessResult: result.
result isDictionary ifTrue:
[ (result includesKey: #error) ifTrue: [ WDException raise: result ]. ]
];
execute.
^result.
]
{ #category : #'private - utilities' }
WebDriver >> sendWithSession: dict to: url using: method [
^ self send: dict to: '/session/' , sessionId , '/' , url using: method
]
{ #category : #accessing }
WebDriver >> session [
"Initializes a new WebDriver session."
capabilities := self constructCapabilities: { } asDictionary.
sessionId ifNil: [
sessionId := (self
send: capabilities
to: 'session'
using: #POST) at: #sessionId ].
^ sessionId
]
{ #category : #accessing }
WebDriver >> session: caps [
sessionId ifNotNil: [ ^ self session ] ifNil: [
capabilities := self constructCapabilities: caps.
sessionId := (self
send: capabilities
to: 'session'
using: #POST) at: #sessionId.
^ sessionId ]
]
{ #category : #'private - utilities' }
WebDriver >> sessionId [
^ sessionId
]
{ #category : #accessing }
WebDriver >> source [
^ (self
send: { }
to:
'session/' , sessionId , '/source'
using: #GET)
]
{ #category : #accessing }
WebDriver >> status [
^ self send: { } to: 'status' using: #GET
]
{ #category : #accessing }
WebDriver >> timeouts [
| toutdata |
toutdata := self send: { } to: 'session/',sessionId,'/timeouts' using: #GET.
^ WDTimeouts new
script: (toutdata at: #script);
pageLoad: (toutdata at: #pageLoad);
implicit: (toutdata at: #implicit).
]
{ #category : #accessing }
WebDriver >> timeouts: timeouts [
self
send: timeouts extract
to: 'session/' , sessionId , 'timeouts'
using: #POST
]
{ #category : #accessing }
WebDriver >> title [
^ self send: { } to: 'session/' , sessionId , '/title' using: #GET
]
{ #category : #accessing }
WebDriver >> url [
^ self send: { } to: 'session/' , sessionId , '/url' using: #GET
]
{ #category : #navigation }
WebDriver >> url: url [
"Navigates the browser to the given URL."
self
send: { (#url -> url) }
to: 'session/' , sessionId , '/url'
using: #POST
]