Check an ES6 API implementation: can you see anything terrible?

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
I have just finished writing an API that implements the FULL w3c protocol for webdrivers.
This is my first attempt to write fully ES6 code, along with async/await. I removed some of the functions here as they were very similar.
Questions:
- Did I make any ES6 disaster?
- Can you see anything clearly terrible in the code?
- I have an inconsistency.. I have
static get Using () return USINGandstatic get KEY () return KEY. They are constants added to a class. Should I haveKEYorKey?USINGorUsing? - Anything I should look at/improve?
I am all ears... thank you!
var request = require('request-promise-native')
const spawn = require('child_process')
var DO = require('deepobject')
const getPort = require('get-port')
var consolelog = require('debug')('webdriver')
const KEY = require('./KEY.js')
const USING =
CSS: 'css selector',
LINK_TEXT: 'link text',
PARTIAL_LINK_TEXT: 'partial link text',
TAG_NAME: 'tag name',
XPATH: 'xpath'
function isObject (p) return typeof p === 'object' && p !== null && !Array.isArray(p)
function checkRes (res)
if (!isObject(res)) throw new Error('Unexpected non-object received from webdriver')
if (typeof res.value === 'undefined') throw new Error('Missing `value` from object returned by webdriver')
return res
function exec (command, commandOptions) , 'ignore'
)
proc.on('error', (err) =>
consolelog(`Could not run $command:`, err)
throw new Error(`Error running the webdriver '$command'`)
)
proc.unref()
process.once('exit', onProcessExit)
let result = new Promise(resolve =>
proc.once('exit', (code, signal) =>
consolelog(`Process $command has exited! Code and signal:`, code, signal)
proc = null
process.removeListener('exit', onProcessExit)
resolve( code, signal )
)
)
return result, killCommand
function onProcessExit ()
consolelog(`Process closed, killing $command`, killCommand)
killCommand('SIGTERM')
function killCommand (signal)
consolelog(`killCommand() called! sending $signal to $command`)
process.removeListener('exit', onProcessExit)
if (proc)
consolelog(`Sending $signal to $command`)
proc.kill(signal)
proc = null
function sleep (ms)
return new Promise(resolve => setTimeout(resolve, ms))
class Browser
constructor (alwaysMatch = , firstMatch = , root = )
// Sanity checks. Things can go pretty bad if these are wrong
if (!isObject(alwaysMatch))
throw new Error('alwaysMatch must be an object')
if (!Array.isArray(firstMatch))
throw new Error('firstmatch parameter must be an array')
if (!isObject(root))
throw new Error('root options must be an object')
this.sessionParameters =
capabilities:
alwaysMatch: alwaysMatch,
firstMatch: firstMatch
// Copy over whatever is specified in `root`
for (var k in root)
if (root.hasOwnProperty(k)) this.sessionParameters[ k ] = root[k]
// Give it a nice, lowercase name
this.name = 'browser'
setAlwaysMatchKey (name, value, force = false)
if (force
addFirstMatch (name, value, force = false)
setRootKey (name, value, force = false)
if (force
getSessionParameters ()
return this.sessionParameters
// Options: port, args, env, stdio
async run (options)
class Chrome extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
// Give it a nice, lowercase name
this.name = 'chrome'
this.setAlwaysMatchKey('chromeOptions.w3c', true, true)
this.setAlwaysMatchKey('browserName', 'chrome')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch.chromeOptions[ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Firefox extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
this.name = 'firefox'
this.setAlwaysMatchKey('moz:firefoxOptions', , true)
this.setAlwaysMatchKey('browserName', 'firefox')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch['moz:firefoxOptions'][ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Selenium extends Browser // eslint-disable-line no-unused-vars
class InputDevice
constructor (id)
this.id = id
class Keyboard extends InputDevice
constructor (id)
super(id)
this.type = 'key'
static get UP ()
return 'keyUp'
static get DOWN ()
return 'keyDown'
tickMethods ()
return
Up: (value) =>
return
type: 'keyUp',
value
,
Down: (value) =>
return
type: 'keyDown',
value
class Pointer extends InputDevice
constructor (id, pointerType)
super(id)
this.pointerType = pointerType
this.type = 'pointer'
static get Type ()
return
MOUSE: 'mouse',
PEN: 'pen',
TOUCH: 'touch'
static get Origin ()
return
VIEWPORT: 'viewport',
POINTER: 'pointer'
tickMethods ()
return
Move: (args) =>
var origin
if (!args.origin)
origin = Pointer.Origin.VIEWPORT
else
if (args.origin instanceof Element)
origin =
'element-6066-11e4-a52e-4f735466cecf': args.origin.id,
ELEMENT: args.origin.id
else
origin = args.origin
if (origin !== Pointer.Origin.VIEWPORT &&
origin !== Pointer.Origin.POINTER)
throw new Error('When using move(), origin must be an element, Pointer.Origin.VIEWPORT or Pointer.Origin.POINTER')
else
return 0,
origin,
x: args.x,
y: args.y
,
Down: (button = 0) =>
return
type: 'pointerDown',
button
,
Up: (button = 0) =>
return
type: 'pointerUp',
button
,
Cancel: () =>
return
type: 'pointerCancel'
class Actions // eslint-disable-line no-unused-vars
constructor (...devices)
var self = this
this.actions =
this._compiled = false
this.compiledActions =
if (Object.keys(devices).length)
this.devices = devices
else
this.devices = [
new Pointer('mouse', Pointer.Type.MOUSE),
new Keyboard('keyboard')
]
this._tickSetters =
get tick ()
return self.tick
,
compile: self.compile.bind(self)
this.devices.forEach((device) =>
var deviceTickMethods = device.tickMethods()
Object.keys(deviceTickMethods).forEach((k) =>
this._tickSetters[device.id + k] = function (...args)
if (!self._currentAction[device.id].virgin)
throw new Error(`Action for device $device.id already defined ($device.id + k) for this tick`)
var res = deviceTickMethods[k].apply(device, args)
self._currentAction[device.id] = res
return self._tickSetters
this._tickSetters['pause'] = function (duration = 0)
self._currentAction[device.id] = type: 'pause', duration
return self._tickSetters
)
)
static get KEY () return KEY
compile ()
if (this._compiled) return
this.compiledActions =
this.devices.forEach((device) =>
var deviceActions = actions:
deviceActions.type = device.type
deviceActions.id = device.id
if (device.type === 'pointer')
deviceActions.parameters = pointerType: device.pointerType
this.actions.forEach((action) =>
deviceActions.actions.push(action[ device.id ])
)
this.compiledActions.push(deviceActions)
)
_setAction (deviceId, action)
this._currentAction[ deviceId ] = action
get tick ()
this._compiled = false
var action =
this.devices.forEach((device) =>
action[ device.id ] = type: 'pause', duration: 0, virgin: true
)
this.actions.push(action)
this._currentAction = action
return this._tickSetters
const FindHelpersMixin = (superClass) => class extends superClass
findElementCss (value)
return this.findElement(Driver.Using.CSS, value)
findElementLinkText (value)
return this.findElement(Driver.Using.LINK_TEXT, value)
findElementPartialLinkText (value)
return this.findElement(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementTagName (value)
return this.findElement(Driver.Using.TAG_NAME, value)
findElementXpath (value)
return this.findElement(Driver.Using.XPATH, value)
findElementsCss (value)
return this.findElements(Driver.Using.CSS, value)
findElementsLinkText (value)
return this.findElements(Driver.Using.LINK_TEXT, value)
findElementsPartialLinkText (value)
return this.findElements(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementsTagName (value)
return this.findElements(Driver.Using.TAG_NAME, value)
findElementsXpath (value)
return this.findElements(Driver.Using.XPATH, value)
var Element = class
constructor (driver, elObject)
var value
this.driver = driver
if (isObject(elObject) && elObject.value) value = elObject.value
else value = elObject
// Sets the ID. Having `element-XXX` and `ELEMENT` as keys to the object means
// that it can be used by switchToFrame()
var idx = 'element-6066-11e4-a52e-4f735466cecf'
this.id = this.ELEMENT = this[idx] = value[idx]
// No ID could be find
if (!this.id) throw new Error('Could not get element ID from element object')
waitFor (timeout = 0, pollInterval = 0)
static get KEY () return KEY
async findElement (using, value)
var el = await this._execute('post', `/element/$this.id/element`, using, value)
return new Element(this.driver, el)
async findElements (using, value)
var els = await this._execute('post', `/element/$this.id/elements`, using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this.driver, v))
async isSelected ()
return !!(await this._execute('get', `/element/$this.id/selected`))
getRect ()
return this._execute('get', `/element/$this.id/rect`)
// REMOVED: A FEW MORE, SIMILAR TO getRect() (one liners)
async isEnabled ()
return !!(await this._execute('get', `/element/$this.id/enabled`))
async sendKeys (text)
var value = text.split('')
await this._execute('post', `/element/$this.id/value`, text, value )
return this
async takeScreenshot (scroll = true)
var data = await this._execute('get', `/element/$this.id/screenshot`, scroll )
return Buffer.from(data, 'base64')
var Driver = class
constructor (browser, options = ) '127.0.0.1'
this._spawn = typeof options.spawn !== 'undefined' ? !!options.spawn : true
this._webDriverRunning = !this._spawn
// Parameters passed onto child process
this._port = options.port
this._env = isObject(options.env) ? options.env : process.env
this._stdio = options.stdio
waitFor (timeout = 0, pollInterval = 0) this._defaultPollTimeout
pollInterval = pollInterval
_ready ()
return !!(this._sessionId && this._webDriverRunning)
async startWebDriver ()
// If it's already connected, nothing to do
if (this._webDriverRunning) return
// If spawning is required, do so
if (this._spawn)
// No port: find a free port
if (!this._port)
this._port = await getPort( host: this._hostname )
// Options: port, args, env, stdio
var res = this._browser.run(
port: this._port,
args: this._args,
env: this._env,
stdio: this._stdio
)
this._killCommand = res.killCommand
this._commandResult = res.result
if (!this.port) this.port = '4444'
this._urlBase = `http://$this._hostname:$this._port/session`
var success = false
for (var i = 0; i < 10; i++)
if (i > 5)
consolelog(`Attempt n. $i to connect to $this._hostname, port $this._port... `)
try
await this.status()
success = true
break
catch (e)
await this.sleep(1000)
if (!success)
throw new Error(`Could not connect to the driver`)
this._webDriverRunning = true
stopWebDriver (signal = 'SIGTERM')
if (this._killCommand)
this._killCommand(signal)
this.killWebDriver('SIGTERM')
this._webDriverRunning = false
inspect ()
return `Driver ip: $this.Name, port: $this._port `
async newSession ()
try catch (e)
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
throw (e)
async deleteSession ()
try
var value = await this._execute('delete', '')
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
return value
catch (e)
throw (e)
async status ()
var _urlBase = `http://$this._hostname:$this._port`
var res = await request.get( url: `$_urlBase/status`, json: true )
return checkRes(res).value
async findElement (using, value)
var el = await this._execute('post', '/element', using, value)
return new Element(this, el)
async findElements (using, value)
var els = await this._execute('post', '/elements', using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this, v))
async performActions (actions)
actions.compile()
await this._execute('post', '/actions', actions: actions.compiledActions )
return this
async releaseActions ()
await this._execute('delete', '/actions')
return this
async sleep (ms)
return sleep(ms)
async _execute (method, command, params)
static get Using () return USING
getTimeouts ()
return this._execute('get', '/timeouts')
async navigateTo (url)
await this._execute('post', '/url', url )
return this
// Mixin the find helpers with Driver and Element
Driver = FindHelpersMixin(Driver)
Element = FindHelpersMixin(Element)
javascript ecmascript-6
add a comment |Â
up vote
1
down vote
favorite
I have just finished writing an API that implements the FULL w3c protocol for webdrivers.
This is my first attempt to write fully ES6 code, along with async/await. I removed some of the functions here as they were very similar.
Questions:
- Did I make any ES6 disaster?
- Can you see anything clearly terrible in the code?
- I have an inconsistency.. I have
static get Using () return USINGandstatic get KEY () return KEY. They are constants added to a class. Should I haveKEYorKey?USINGorUsing? - Anything I should look at/improve?
I am all ears... thank you!
var request = require('request-promise-native')
const spawn = require('child_process')
var DO = require('deepobject')
const getPort = require('get-port')
var consolelog = require('debug')('webdriver')
const KEY = require('./KEY.js')
const USING =
CSS: 'css selector',
LINK_TEXT: 'link text',
PARTIAL_LINK_TEXT: 'partial link text',
TAG_NAME: 'tag name',
XPATH: 'xpath'
function isObject (p) return typeof p === 'object' && p !== null && !Array.isArray(p)
function checkRes (res)
if (!isObject(res)) throw new Error('Unexpected non-object received from webdriver')
if (typeof res.value === 'undefined') throw new Error('Missing `value` from object returned by webdriver')
return res
function exec (command, commandOptions) , 'ignore'
)
proc.on('error', (err) =>
consolelog(`Could not run $command:`, err)
throw new Error(`Error running the webdriver '$command'`)
)
proc.unref()
process.once('exit', onProcessExit)
let result = new Promise(resolve =>
proc.once('exit', (code, signal) =>
consolelog(`Process $command has exited! Code and signal:`, code, signal)
proc = null
process.removeListener('exit', onProcessExit)
resolve( code, signal )
)
)
return result, killCommand
function onProcessExit ()
consolelog(`Process closed, killing $command`, killCommand)
killCommand('SIGTERM')
function killCommand (signal)
consolelog(`killCommand() called! sending $signal to $command`)
process.removeListener('exit', onProcessExit)
if (proc)
consolelog(`Sending $signal to $command`)
proc.kill(signal)
proc = null
function sleep (ms)
return new Promise(resolve => setTimeout(resolve, ms))
class Browser
constructor (alwaysMatch = , firstMatch = , root = )
// Sanity checks. Things can go pretty bad if these are wrong
if (!isObject(alwaysMatch))
throw new Error('alwaysMatch must be an object')
if (!Array.isArray(firstMatch))
throw new Error('firstmatch parameter must be an array')
if (!isObject(root))
throw new Error('root options must be an object')
this.sessionParameters =
capabilities:
alwaysMatch: alwaysMatch,
firstMatch: firstMatch
// Copy over whatever is specified in `root`
for (var k in root)
if (root.hasOwnProperty(k)) this.sessionParameters[ k ] = root[k]
// Give it a nice, lowercase name
this.name = 'browser'
setAlwaysMatchKey (name, value, force = false)
if (force
addFirstMatch (name, value, force = false)
setRootKey (name, value, force = false)
if (force
getSessionParameters ()
return this.sessionParameters
// Options: port, args, env, stdio
async run (options)
class Chrome extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
// Give it a nice, lowercase name
this.name = 'chrome'
this.setAlwaysMatchKey('chromeOptions.w3c', true, true)
this.setAlwaysMatchKey('browserName', 'chrome')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch.chromeOptions[ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Firefox extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
this.name = 'firefox'
this.setAlwaysMatchKey('moz:firefoxOptions', , true)
this.setAlwaysMatchKey('browserName', 'firefox')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch['moz:firefoxOptions'][ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Selenium extends Browser // eslint-disable-line no-unused-vars
class InputDevice
constructor (id)
this.id = id
class Keyboard extends InputDevice
constructor (id)
super(id)
this.type = 'key'
static get UP ()
return 'keyUp'
static get DOWN ()
return 'keyDown'
tickMethods ()
return
Up: (value) =>
return
type: 'keyUp',
value
,
Down: (value) =>
return
type: 'keyDown',
value
class Pointer extends InputDevice
constructor (id, pointerType)
super(id)
this.pointerType = pointerType
this.type = 'pointer'
static get Type ()
return
MOUSE: 'mouse',
PEN: 'pen',
TOUCH: 'touch'
static get Origin ()
return
VIEWPORT: 'viewport',
POINTER: 'pointer'
tickMethods ()
return
Move: (args) =>
var origin
if (!args.origin)
origin = Pointer.Origin.VIEWPORT
else
if (args.origin instanceof Element)
origin =
'element-6066-11e4-a52e-4f735466cecf': args.origin.id,
ELEMENT: args.origin.id
else
origin = args.origin
if (origin !== Pointer.Origin.VIEWPORT &&
origin !== Pointer.Origin.POINTER)
throw new Error('When using move(), origin must be an element, Pointer.Origin.VIEWPORT or Pointer.Origin.POINTER')
else
return 0,
origin,
x: args.x,
y: args.y
,
Down: (button = 0) =>
return
type: 'pointerDown',
button
,
Up: (button = 0) =>
return
type: 'pointerUp',
button
,
Cancel: () =>
return
type: 'pointerCancel'
class Actions // eslint-disable-line no-unused-vars
constructor (...devices)
var self = this
this.actions =
this._compiled = false
this.compiledActions =
if (Object.keys(devices).length)
this.devices = devices
else
this.devices = [
new Pointer('mouse', Pointer.Type.MOUSE),
new Keyboard('keyboard')
]
this._tickSetters =
get tick ()
return self.tick
,
compile: self.compile.bind(self)
this.devices.forEach((device) =>
var deviceTickMethods = device.tickMethods()
Object.keys(deviceTickMethods).forEach((k) =>
this._tickSetters[device.id + k] = function (...args)
if (!self._currentAction[device.id].virgin)
throw new Error(`Action for device $device.id already defined ($device.id + k) for this tick`)
var res = deviceTickMethods[k].apply(device, args)
self._currentAction[device.id] = res
return self._tickSetters
this._tickSetters['pause'] = function (duration = 0)
self._currentAction[device.id] = type: 'pause', duration
return self._tickSetters
)
)
static get KEY () return KEY
compile ()
if (this._compiled) return
this.compiledActions =
this.devices.forEach((device) =>
var deviceActions = actions:
deviceActions.type = device.type
deviceActions.id = device.id
if (device.type === 'pointer')
deviceActions.parameters = pointerType: device.pointerType
this.actions.forEach((action) =>
deviceActions.actions.push(action[ device.id ])
)
this.compiledActions.push(deviceActions)
)
_setAction (deviceId, action)
this._currentAction[ deviceId ] = action
get tick ()
this._compiled = false
var action =
this.devices.forEach((device) =>
action[ device.id ] = type: 'pause', duration: 0, virgin: true
)
this.actions.push(action)
this._currentAction = action
return this._tickSetters
const FindHelpersMixin = (superClass) => class extends superClass
findElementCss (value)
return this.findElement(Driver.Using.CSS, value)
findElementLinkText (value)
return this.findElement(Driver.Using.LINK_TEXT, value)
findElementPartialLinkText (value)
return this.findElement(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementTagName (value)
return this.findElement(Driver.Using.TAG_NAME, value)
findElementXpath (value)
return this.findElement(Driver.Using.XPATH, value)
findElementsCss (value)
return this.findElements(Driver.Using.CSS, value)
findElementsLinkText (value)
return this.findElements(Driver.Using.LINK_TEXT, value)
findElementsPartialLinkText (value)
return this.findElements(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementsTagName (value)
return this.findElements(Driver.Using.TAG_NAME, value)
findElementsXpath (value)
return this.findElements(Driver.Using.XPATH, value)
var Element = class
constructor (driver, elObject)
var value
this.driver = driver
if (isObject(elObject) && elObject.value) value = elObject.value
else value = elObject
// Sets the ID. Having `element-XXX` and `ELEMENT` as keys to the object means
// that it can be used by switchToFrame()
var idx = 'element-6066-11e4-a52e-4f735466cecf'
this.id = this.ELEMENT = this[idx] = value[idx]
// No ID could be find
if (!this.id) throw new Error('Could not get element ID from element object')
waitFor (timeout = 0, pollInterval = 0)
static get KEY () return KEY
async findElement (using, value)
var el = await this._execute('post', `/element/$this.id/element`, using, value)
return new Element(this.driver, el)
async findElements (using, value)
var els = await this._execute('post', `/element/$this.id/elements`, using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this.driver, v))
async isSelected ()
return !!(await this._execute('get', `/element/$this.id/selected`))
getRect ()
return this._execute('get', `/element/$this.id/rect`)
// REMOVED: A FEW MORE, SIMILAR TO getRect() (one liners)
async isEnabled ()
return !!(await this._execute('get', `/element/$this.id/enabled`))
async sendKeys (text)
var value = text.split('')
await this._execute('post', `/element/$this.id/value`, text, value )
return this
async takeScreenshot (scroll = true)
var data = await this._execute('get', `/element/$this.id/screenshot`, scroll )
return Buffer.from(data, 'base64')
var Driver = class
constructor (browser, options = ) '127.0.0.1'
this._spawn = typeof options.spawn !== 'undefined' ? !!options.spawn : true
this._webDriverRunning = !this._spawn
// Parameters passed onto child process
this._port = options.port
this._env = isObject(options.env) ? options.env : process.env
this._stdio = options.stdio
waitFor (timeout = 0, pollInterval = 0) this._defaultPollTimeout
pollInterval = pollInterval
_ready ()
return !!(this._sessionId && this._webDriverRunning)
async startWebDriver ()
// If it's already connected, nothing to do
if (this._webDriverRunning) return
// If spawning is required, do so
if (this._spawn)
// No port: find a free port
if (!this._port)
this._port = await getPort( host: this._hostname )
// Options: port, args, env, stdio
var res = this._browser.run(
port: this._port,
args: this._args,
env: this._env,
stdio: this._stdio
)
this._killCommand = res.killCommand
this._commandResult = res.result
if (!this.port) this.port = '4444'
this._urlBase = `http://$this._hostname:$this._port/session`
var success = false
for (var i = 0; i < 10; i++)
if (i > 5)
consolelog(`Attempt n. $i to connect to $this._hostname, port $this._port... `)
try
await this.status()
success = true
break
catch (e)
await this.sleep(1000)
if (!success)
throw new Error(`Could not connect to the driver`)
this._webDriverRunning = true
stopWebDriver (signal = 'SIGTERM')
if (this._killCommand)
this._killCommand(signal)
this.killWebDriver('SIGTERM')
this._webDriverRunning = false
inspect ()
return `Driver ip: $this.Name, port: $this._port `
async newSession ()
try catch (e)
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
throw (e)
async deleteSession ()
try
var value = await this._execute('delete', '')
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
return value
catch (e)
throw (e)
async status ()
var _urlBase = `http://$this._hostname:$this._port`
var res = await request.get( url: `$_urlBase/status`, json: true )
return checkRes(res).value
async findElement (using, value)
var el = await this._execute('post', '/element', using, value)
return new Element(this, el)
async findElements (using, value)
var els = await this._execute('post', '/elements', using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this, v))
async performActions (actions)
actions.compile()
await this._execute('post', '/actions', actions: actions.compiledActions )
return this
async releaseActions ()
await this._execute('delete', '/actions')
return this
async sleep (ms)
return sleep(ms)
async _execute (method, command, params)
static get Using () return USING
getTimeouts ()
return this._execute('get', '/timeouts')
async navigateTo (url)
await this._execute('post', '/url', url )
return this
// Mixin the find helpers with Driver and Element
Driver = FindHelpersMixin(Driver)
Element = FindHelpersMixin(Element)
javascript ecmascript-6
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I have just finished writing an API that implements the FULL w3c protocol for webdrivers.
This is my first attempt to write fully ES6 code, along with async/await. I removed some of the functions here as they were very similar.
Questions:
- Did I make any ES6 disaster?
- Can you see anything clearly terrible in the code?
- I have an inconsistency.. I have
static get Using () return USINGandstatic get KEY () return KEY. They are constants added to a class. Should I haveKEYorKey?USINGorUsing? - Anything I should look at/improve?
I am all ears... thank you!
var request = require('request-promise-native')
const spawn = require('child_process')
var DO = require('deepobject')
const getPort = require('get-port')
var consolelog = require('debug')('webdriver')
const KEY = require('./KEY.js')
const USING =
CSS: 'css selector',
LINK_TEXT: 'link text',
PARTIAL_LINK_TEXT: 'partial link text',
TAG_NAME: 'tag name',
XPATH: 'xpath'
function isObject (p) return typeof p === 'object' && p !== null && !Array.isArray(p)
function checkRes (res)
if (!isObject(res)) throw new Error('Unexpected non-object received from webdriver')
if (typeof res.value === 'undefined') throw new Error('Missing `value` from object returned by webdriver')
return res
function exec (command, commandOptions) , 'ignore'
)
proc.on('error', (err) =>
consolelog(`Could not run $command:`, err)
throw new Error(`Error running the webdriver '$command'`)
)
proc.unref()
process.once('exit', onProcessExit)
let result = new Promise(resolve =>
proc.once('exit', (code, signal) =>
consolelog(`Process $command has exited! Code and signal:`, code, signal)
proc = null
process.removeListener('exit', onProcessExit)
resolve( code, signal )
)
)
return result, killCommand
function onProcessExit ()
consolelog(`Process closed, killing $command`, killCommand)
killCommand('SIGTERM')
function killCommand (signal)
consolelog(`killCommand() called! sending $signal to $command`)
process.removeListener('exit', onProcessExit)
if (proc)
consolelog(`Sending $signal to $command`)
proc.kill(signal)
proc = null
function sleep (ms)
return new Promise(resolve => setTimeout(resolve, ms))
class Browser
constructor (alwaysMatch = , firstMatch = , root = )
// Sanity checks. Things can go pretty bad if these are wrong
if (!isObject(alwaysMatch))
throw new Error('alwaysMatch must be an object')
if (!Array.isArray(firstMatch))
throw new Error('firstmatch parameter must be an array')
if (!isObject(root))
throw new Error('root options must be an object')
this.sessionParameters =
capabilities:
alwaysMatch: alwaysMatch,
firstMatch: firstMatch
// Copy over whatever is specified in `root`
for (var k in root)
if (root.hasOwnProperty(k)) this.sessionParameters[ k ] = root[k]
// Give it a nice, lowercase name
this.name = 'browser'
setAlwaysMatchKey (name, value, force = false)
if (force
addFirstMatch (name, value, force = false)
setRootKey (name, value, force = false)
if (force
getSessionParameters ()
return this.sessionParameters
// Options: port, args, env, stdio
async run (options)
class Chrome extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
// Give it a nice, lowercase name
this.name = 'chrome'
this.setAlwaysMatchKey('chromeOptions.w3c', true, true)
this.setAlwaysMatchKey('browserName', 'chrome')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch.chromeOptions[ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Firefox extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
this.name = 'firefox'
this.setAlwaysMatchKey('moz:firefoxOptions', , true)
this.setAlwaysMatchKey('browserName', 'firefox')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch['moz:firefoxOptions'][ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Selenium extends Browser // eslint-disable-line no-unused-vars
class InputDevice
constructor (id)
this.id = id
class Keyboard extends InputDevice
constructor (id)
super(id)
this.type = 'key'
static get UP ()
return 'keyUp'
static get DOWN ()
return 'keyDown'
tickMethods ()
return
Up: (value) =>
return
type: 'keyUp',
value
,
Down: (value) =>
return
type: 'keyDown',
value
class Pointer extends InputDevice
constructor (id, pointerType)
super(id)
this.pointerType = pointerType
this.type = 'pointer'
static get Type ()
return
MOUSE: 'mouse',
PEN: 'pen',
TOUCH: 'touch'
static get Origin ()
return
VIEWPORT: 'viewport',
POINTER: 'pointer'
tickMethods ()
return
Move: (args) =>
var origin
if (!args.origin)
origin = Pointer.Origin.VIEWPORT
else
if (args.origin instanceof Element)
origin =
'element-6066-11e4-a52e-4f735466cecf': args.origin.id,
ELEMENT: args.origin.id
else
origin = args.origin
if (origin !== Pointer.Origin.VIEWPORT &&
origin !== Pointer.Origin.POINTER)
throw new Error('When using move(), origin must be an element, Pointer.Origin.VIEWPORT or Pointer.Origin.POINTER')
else
return 0,
origin,
x: args.x,
y: args.y
,
Down: (button = 0) =>
return
type: 'pointerDown',
button
,
Up: (button = 0) =>
return
type: 'pointerUp',
button
,
Cancel: () =>
return
type: 'pointerCancel'
class Actions // eslint-disable-line no-unused-vars
constructor (...devices)
var self = this
this.actions =
this._compiled = false
this.compiledActions =
if (Object.keys(devices).length)
this.devices = devices
else
this.devices = [
new Pointer('mouse', Pointer.Type.MOUSE),
new Keyboard('keyboard')
]
this._tickSetters =
get tick ()
return self.tick
,
compile: self.compile.bind(self)
this.devices.forEach((device) =>
var deviceTickMethods = device.tickMethods()
Object.keys(deviceTickMethods).forEach((k) =>
this._tickSetters[device.id + k] = function (...args)
if (!self._currentAction[device.id].virgin)
throw new Error(`Action for device $device.id already defined ($device.id + k) for this tick`)
var res = deviceTickMethods[k].apply(device, args)
self._currentAction[device.id] = res
return self._tickSetters
this._tickSetters['pause'] = function (duration = 0)
self._currentAction[device.id] = type: 'pause', duration
return self._tickSetters
)
)
static get KEY () return KEY
compile ()
if (this._compiled) return
this.compiledActions =
this.devices.forEach((device) =>
var deviceActions = actions:
deviceActions.type = device.type
deviceActions.id = device.id
if (device.type === 'pointer')
deviceActions.parameters = pointerType: device.pointerType
this.actions.forEach((action) =>
deviceActions.actions.push(action[ device.id ])
)
this.compiledActions.push(deviceActions)
)
_setAction (deviceId, action)
this._currentAction[ deviceId ] = action
get tick ()
this._compiled = false
var action =
this.devices.forEach((device) =>
action[ device.id ] = type: 'pause', duration: 0, virgin: true
)
this.actions.push(action)
this._currentAction = action
return this._tickSetters
const FindHelpersMixin = (superClass) => class extends superClass
findElementCss (value)
return this.findElement(Driver.Using.CSS, value)
findElementLinkText (value)
return this.findElement(Driver.Using.LINK_TEXT, value)
findElementPartialLinkText (value)
return this.findElement(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementTagName (value)
return this.findElement(Driver.Using.TAG_NAME, value)
findElementXpath (value)
return this.findElement(Driver.Using.XPATH, value)
findElementsCss (value)
return this.findElements(Driver.Using.CSS, value)
findElementsLinkText (value)
return this.findElements(Driver.Using.LINK_TEXT, value)
findElementsPartialLinkText (value)
return this.findElements(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementsTagName (value)
return this.findElements(Driver.Using.TAG_NAME, value)
findElementsXpath (value)
return this.findElements(Driver.Using.XPATH, value)
var Element = class
constructor (driver, elObject)
var value
this.driver = driver
if (isObject(elObject) && elObject.value) value = elObject.value
else value = elObject
// Sets the ID. Having `element-XXX` and `ELEMENT` as keys to the object means
// that it can be used by switchToFrame()
var idx = 'element-6066-11e4-a52e-4f735466cecf'
this.id = this.ELEMENT = this[idx] = value[idx]
// No ID could be find
if (!this.id) throw new Error('Could not get element ID from element object')
waitFor (timeout = 0, pollInterval = 0)
static get KEY () return KEY
async findElement (using, value)
var el = await this._execute('post', `/element/$this.id/element`, using, value)
return new Element(this.driver, el)
async findElements (using, value)
var els = await this._execute('post', `/element/$this.id/elements`, using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this.driver, v))
async isSelected ()
return !!(await this._execute('get', `/element/$this.id/selected`))
getRect ()
return this._execute('get', `/element/$this.id/rect`)
// REMOVED: A FEW MORE, SIMILAR TO getRect() (one liners)
async isEnabled ()
return !!(await this._execute('get', `/element/$this.id/enabled`))
async sendKeys (text)
var value = text.split('')
await this._execute('post', `/element/$this.id/value`, text, value )
return this
async takeScreenshot (scroll = true)
var data = await this._execute('get', `/element/$this.id/screenshot`, scroll )
return Buffer.from(data, 'base64')
var Driver = class
constructor (browser, options = ) '127.0.0.1'
this._spawn = typeof options.spawn !== 'undefined' ? !!options.spawn : true
this._webDriverRunning = !this._spawn
// Parameters passed onto child process
this._port = options.port
this._env = isObject(options.env) ? options.env : process.env
this._stdio = options.stdio
waitFor (timeout = 0, pollInterval = 0) this._defaultPollTimeout
pollInterval = pollInterval
_ready ()
return !!(this._sessionId && this._webDriverRunning)
async startWebDriver ()
// If it's already connected, nothing to do
if (this._webDriverRunning) return
// If spawning is required, do so
if (this._spawn)
// No port: find a free port
if (!this._port)
this._port = await getPort( host: this._hostname )
// Options: port, args, env, stdio
var res = this._browser.run(
port: this._port,
args: this._args,
env: this._env,
stdio: this._stdio
)
this._killCommand = res.killCommand
this._commandResult = res.result
if (!this.port) this.port = '4444'
this._urlBase = `http://$this._hostname:$this._port/session`
var success = false
for (var i = 0; i < 10; i++)
if (i > 5)
consolelog(`Attempt n. $i to connect to $this._hostname, port $this._port... `)
try
await this.status()
success = true
break
catch (e)
await this.sleep(1000)
if (!success)
throw new Error(`Could not connect to the driver`)
this._webDriverRunning = true
stopWebDriver (signal = 'SIGTERM')
if (this._killCommand)
this._killCommand(signal)
this.killWebDriver('SIGTERM')
this._webDriverRunning = false
inspect ()
return `Driver ip: $this.Name, port: $this._port `
async newSession ()
try catch (e)
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
throw (e)
async deleteSession ()
try
var value = await this._execute('delete', '')
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
return value
catch (e)
throw (e)
async status ()
var _urlBase = `http://$this._hostname:$this._port`
var res = await request.get( url: `$_urlBase/status`, json: true )
return checkRes(res).value
async findElement (using, value)
var el = await this._execute('post', '/element', using, value)
return new Element(this, el)
async findElements (using, value)
var els = await this._execute('post', '/elements', using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this, v))
async performActions (actions)
actions.compile()
await this._execute('post', '/actions', actions: actions.compiledActions )
return this
async releaseActions ()
await this._execute('delete', '/actions')
return this
async sleep (ms)
return sleep(ms)
async _execute (method, command, params)
static get Using () return USING
getTimeouts ()
return this._execute('get', '/timeouts')
async navigateTo (url)
await this._execute('post', '/url', url )
return this
// Mixin the find helpers with Driver and Element
Driver = FindHelpersMixin(Driver)
Element = FindHelpersMixin(Element)
javascript ecmascript-6
I have just finished writing an API that implements the FULL w3c protocol for webdrivers.
This is my first attempt to write fully ES6 code, along with async/await. I removed some of the functions here as they were very similar.
Questions:
- Did I make any ES6 disaster?
- Can you see anything clearly terrible in the code?
- I have an inconsistency.. I have
static get Using () return USINGandstatic get KEY () return KEY. They are constants added to a class. Should I haveKEYorKey?USINGorUsing? - Anything I should look at/improve?
I am all ears... thank you!
var request = require('request-promise-native')
const spawn = require('child_process')
var DO = require('deepobject')
const getPort = require('get-port')
var consolelog = require('debug')('webdriver')
const KEY = require('./KEY.js')
const USING =
CSS: 'css selector',
LINK_TEXT: 'link text',
PARTIAL_LINK_TEXT: 'partial link text',
TAG_NAME: 'tag name',
XPATH: 'xpath'
function isObject (p) return typeof p === 'object' && p !== null && !Array.isArray(p)
function checkRes (res)
if (!isObject(res)) throw new Error('Unexpected non-object received from webdriver')
if (typeof res.value === 'undefined') throw new Error('Missing `value` from object returned by webdriver')
return res
function exec (command, commandOptions) , 'ignore'
)
proc.on('error', (err) =>
consolelog(`Could not run $command:`, err)
throw new Error(`Error running the webdriver '$command'`)
)
proc.unref()
process.once('exit', onProcessExit)
let result = new Promise(resolve =>
proc.once('exit', (code, signal) =>
consolelog(`Process $command has exited! Code and signal:`, code, signal)
proc = null
process.removeListener('exit', onProcessExit)
resolve( code, signal )
)
)
return result, killCommand
function onProcessExit ()
consolelog(`Process closed, killing $command`, killCommand)
killCommand('SIGTERM')
function killCommand (signal)
consolelog(`killCommand() called! sending $signal to $command`)
process.removeListener('exit', onProcessExit)
if (proc)
consolelog(`Sending $signal to $command`)
proc.kill(signal)
proc = null
function sleep (ms)
return new Promise(resolve => setTimeout(resolve, ms))
class Browser
constructor (alwaysMatch = , firstMatch = , root = )
// Sanity checks. Things can go pretty bad if these are wrong
if (!isObject(alwaysMatch))
throw new Error('alwaysMatch must be an object')
if (!Array.isArray(firstMatch))
throw new Error('firstmatch parameter must be an array')
if (!isObject(root))
throw new Error('root options must be an object')
this.sessionParameters =
capabilities:
alwaysMatch: alwaysMatch,
firstMatch: firstMatch
// Copy over whatever is specified in `root`
for (var k in root)
if (root.hasOwnProperty(k)) this.sessionParameters[ k ] = root[k]
// Give it a nice, lowercase name
this.name = 'browser'
setAlwaysMatchKey (name, value, force = false)
if (force
addFirstMatch (name, value, force = false)
setRootKey (name, value, force = false)
if (force
getSessionParameters ()
return this.sessionParameters
// Options: port, args, env, stdio
async run (options)
class Chrome extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
// Give it a nice, lowercase name
this.name = 'chrome'
this.setAlwaysMatchKey('chromeOptions.w3c', true, true)
this.setAlwaysMatchKey('browserName', 'chrome')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch.chromeOptions[ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Firefox extends Browser // eslint-disable-line no-unused-vars
constructor (alwaysMatch = , firstMatch = , root = , specific = )
super(...arguments)
this.name = 'firefox'
this.setAlwaysMatchKey('moz:firefoxOptions', , true)
this.setAlwaysMatchKey('browserName', 'firefox')
for (var k in specific)
if (specific.hasOwnProperty(k))
this.alwaysMatch['moz:firefoxOptions'][ k ] = specific[ k ]
run (options)
var executable = process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver'
options.args.push('--port=' + options.port)
return exec(executable, options)
class Selenium extends Browser // eslint-disable-line no-unused-vars
class InputDevice
constructor (id)
this.id = id
class Keyboard extends InputDevice
constructor (id)
super(id)
this.type = 'key'
static get UP ()
return 'keyUp'
static get DOWN ()
return 'keyDown'
tickMethods ()
return
Up: (value) =>
return
type: 'keyUp',
value
,
Down: (value) =>
return
type: 'keyDown',
value
class Pointer extends InputDevice
constructor (id, pointerType)
super(id)
this.pointerType = pointerType
this.type = 'pointer'
static get Type ()
return
MOUSE: 'mouse',
PEN: 'pen',
TOUCH: 'touch'
static get Origin ()
return
VIEWPORT: 'viewport',
POINTER: 'pointer'
tickMethods ()
return
Move: (args) =>
var origin
if (!args.origin)
origin = Pointer.Origin.VIEWPORT
else
if (args.origin instanceof Element)
origin =
'element-6066-11e4-a52e-4f735466cecf': args.origin.id,
ELEMENT: args.origin.id
else
origin = args.origin
if (origin !== Pointer.Origin.VIEWPORT &&
origin !== Pointer.Origin.POINTER)
throw new Error('When using move(), origin must be an element, Pointer.Origin.VIEWPORT or Pointer.Origin.POINTER')
else
return 0,
origin,
x: args.x,
y: args.y
,
Down: (button = 0) =>
return
type: 'pointerDown',
button
,
Up: (button = 0) =>
return
type: 'pointerUp',
button
,
Cancel: () =>
return
type: 'pointerCancel'
class Actions // eslint-disable-line no-unused-vars
constructor (...devices)
var self = this
this.actions =
this._compiled = false
this.compiledActions =
if (Object.keys(devices).length)
this.devices = devices
else
this.devices = [
new Pointer('mouse', Pointer.Type.MOUSE),
new Keyboard('keyboard')
]
this._tickSetters =
get tick ()
return self.tick
,
compile: self.compile.bind(self)
this.devices.forEach((device) =>
var deviceTickMethods = device.tickMethods()
Object.keys(deviceTickMethods).forEach((k) =>
this._tickSetters[device.id + k] = function (...args)
if (!self._currentAction[device.id].virgin)
throw new Error(`Action for device $device.id already defined ($device.id + k) for this tick`)
var res = deviceTickMethods[k].apply(device, args)
self._currentAction[device.id] = res
return self._tickSetters
this._tickSetters['pause'] = function (duration = 0)
self._currentAction[device.id] = type: 'pause', duration
return self._tickSetters
)
)
static get KEY () return KEY
compile ()
if (this._compiled) return
this.compiledActions =
this.devices.forEach((device) =>
var deviceActions = actions:
deviceActions.type = device.type
deviceActions.id = device.id
if (device.type === 'pointer')
deviceActions.parameters = pointerType: device.pointerType
this.actions.forEach((action) =>
deviceActions.actions.push(action[ device.id ])
)
this.compiledActions.push(deviceActions)
)
_setAction (deviceId, action)
this._currentAction[ deviceId ] = action
get tick ()
this._compiled = false
var action =
this.devices.forEach((device) =>
action[ device.id ] = type: 'pause', duration: 0, virgin: true
)
this.actions.push(action)
this._currentAction = action
return this._tickSetters
const FindHelpersMixin = (superClass) => class extends superClass
findElementCss (value)
return this.findElement(Driver.Using.CSS, value)
findElementLinkText (value)
return this.findElement(Driver.Using.LINK_TEXT, value)
findElementPartialLinkText (value)
return this.findElement(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementTagName (value)
return this.findElement(Driver.Using.TAG_NAME, value)
findElementXpath (value)
return this.findElement(Driver.Using.XPATH, value)
findElementsCss (value)
return this.findElements(Driver.Using.CSS, value)
findElementsLinkText (value)
return this.findElements(Driver.Using.LINK_TEXT, value)
findElementsPartialLinkText (value)
return this.findElements(Driver.Using.PARTIAL_LINK_TEXT, value)
findElementsTagName (value)
return this.findElements(Driver.Using.TAG_NAME, value)
findElementsXpath (value)
return this.findElements(Driver.Using.XPATH, value)
var Element = class
constructor (driver, elObject)
var value
this.driver = driver
if (isObject(elObject) && elObject.value) value = elObject.value
else value = elObject
// Sets the ID. Having `element-XXX` and `ELEMENT` as keys to the object means
// that it can be used by switchToFrame()
var idx = 'element-6066-11e4-a52e-4f735466cecf'
this.id = this.ELEMENT = this[idx] = value[idx]
// No ID could be find
if (!this.id) throw new Error('Could not get element ID from element object')
waitFor (timeout = 0, pollInterval = 0)
static get KEY () return KEY
async findElement (using, value)
var el = await this._execute('post', `/element/$this.id/element`, using, value)
return new Element(this.driver, el)
async findElements (using, value)
var els = await this._execute('post', `/element/$this.id/elements`, using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this.driver, v))
async isSelected ()
return !!(await this._execute('get', `/element/$this.id/selected`))
getRect ()
return this._execute('get', `/element/$this.id/rect`)
// REMOVED: A FEW MORE, SIMILAR TO getRect() (one liners)
async isEnabled ()
return !!(await this._execute('get', `/element/$this.id/enabled`))
async sendKeys (text)
var value = text.split('')
await this._execute('post', `/element/$this.id/value`, text, value )
return this
async takeScreenshot (scroll = true)
var data = await this._execute('get', `/element/$this.id/screenshot`, scroll )
return Buffer.from(data, 'base64')
var Driver = class
constructor (browser, options = ) '127.0.0.1'
this._spawn = typeof options.spawn !== 'undefined' ? !!options.spawn : true
this._webDriverRunning = !this._spawn
// Parameters passed onto child process
this._port = options.port
this._env = isObject(options.env) ? options.env : process.env
this._stdio = options.stdio
waitFor (timeout = 0, pollInterval = 0) this._defaultPollTimeout
pollInterval = pollInterval
_ready ()
return !!(this._sessionId && this._webDriverRunning)
async startWebDriver ()
// If it's already connected, nothing to do
if (this._webDriverRunning) return
// If spawning is required, do so
if (this._spawn)
// No port: find a free port
if (!this._port)
this._port = await getPort( host: this._hostname )
// Options: port, args, env, stdio
var res = this._browser.run(
port: this._port,
args: this._args,
env: this._env,
stdio: this._stdio
)
this._killCommand = res.killCommand
this._commandResult = res.result
if (!this.port) this.port = '4444'
this._urlBase = `http://$this._hostname:$this._port/session`
var success = false
for (var i = 0; i < 10; i++)
if (i > 5)
consolelog(`Attempt n. $i to connect to $this._hostname, port $this._port... `)
try
await this.status()
success = true
break
catch (e)
await this.sleep(1000)
if (!success)
throw new Error(`Could not connect to the driver`)
this._webDriverRunning = true
stopWebDriver (signal = 'SIGTERM')
if (this._killCommand)
this._killCommand(signal)
this.killWebDriver('SIGTERM')
this._webDriverRunning = false
inspect ()
return `Driver ip: $this.Name, port: $this._port `
async newSession ()
try catch (e)
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
throw (e)
async deleteSession ()
try
var value = await this._execute('delete', '')
this._sessionId = null
this._sessionData =
this._urlBase = `http://$this._hostname:$this._port/session`
return value
catch (e)
throw (e)
async status ()
var _urlBase = `http://$this._hostname:$this._port`
var res = await request.get( url: `$_urlBase/status`, json: true )
return checkRes(res).value
async findElement (using, value)
var el = await this._execute('post', '/element', using, value)
return new Element(this, el)
async findElements (using, value)
var els = await this._execute('post', '/elements', using, value)
if (!Array.isArray(els)) throw new Error('Result from findElements must be an array')
return els.map((v) => new Element(this, v))
async performActions (actions)
actions.compile()
await this._execute('post', '/actions', actions: actions.compiledActions )
return this
async releaseActions ()
await this._execute('delete', '/actions')
return this
async sleep (ms)
return sleep(ms)
async _execute (method, command, params)
static get Using () return USING
getTimeouts ()
return this._execute('get', '/timeouts')
async navigateTo (url)
await this._execute('post', '/url', url )
return this
// Mixin the find helpers with Driver and Element
Driver = FindHelpersMixin(Driver)
Element = FindHelpersMixin(Element)
javascript ecmascript-6
asked Jan 28 at 16:02
Merc
22219
22219
add a comment |Â
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186203%2fcheck-an-es6-api-implementation-can-you-see-anything-terrible%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password