Skip to content

Commit

Permalink
Init commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaridmargolin committed Dec 26, 2016
0 parents commit 594bd47
Show file tree
Hide file tree
Showing 13 changed files with 1,202 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
25 changes: 25 additions & 0 deletions .gitignore
@@ -0,0 +1,25 @@
.DS_Store
.grunt

lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz

pids
logs
results

npm-debug.log
sauce_connect.log

/.nyc_output
/node_modules
/bower_components
/coverage
/dist
/docs
23 changes: 23 additions & 0 deletions .travis.yml
@@ -0,0 +1,23 @@
sudo: required
dist: trusty
addons:
apt:
sources:
- google-chrome
packages:
- google-chrome-stable

language: node_js
node_js:
- 7.0.0

before_script:
- export DBUS_SESSION_BUS_ADDRESS=/dev/null
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sleep 3 # give xvfb some time to start

script: npm run coverage

after_success:
- npm run coveralls
47 changes: 47 additions & 0 deletions README.md
@@ -0,0 +1,47 @@
<h1 align="center">wd-elements</h1>
<div align="center">
<p>Better elements for selenium-webdriver.</p>
<div>
<a href="https://travis-ci.org/jaridmargolin/wd-elements"><img src="https://travis-ci.org/jaridmargolin/wd-elements.svg?branch=master" alt="Build Status"></a>
<a href="https://coveralls.io/github/jaridmargolin/wd-elements?branch=master"><img src="https://coveralls.io/repos/github/jaridmargolin/wd-elements/badge.svg?branch=master" alt="Coverage Status"></a>
<a href="http://standardjs.com/"><img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg" alt="Standard - JavaScript Style Guide"></a>
</div>
<div>
<a href="https://npmjs.org/package/wd-elements"><img src="https://img.shields.io/npm/v/wd-elements.svg" alt="NPM wd-elements package"></a>
<a href="https://david-dm.org/jaridmargolin/wd-elements"><img src="https://david-dm.org/jaridmargolin/wd-elements.svg" alt="Dependency Status"></a>
<a href="https://david-dm.org/jaridmargolin/wd-elements#info=devDependencies"><img src="https://david-dm.org/jaridmargolin/wd-elements/dev-status.svg" alt="devDependency Status"></a>
</div>
</div>
<br>

## Why

Extensibility. As of now, **selenium-webdriver** exposes a single class, `WebElement`, yet we know the DOM is constructed of more than 58 different elements (thank you google). Now take into account the explosion of CustomElements (polymer, react, etc...). Our front end is built using components, and our integration tests should as well.

The goal of **wd-elements** is to provide a sane and consistent environment to author integration tests around the concept of custom elements.

## Basic Usage

```js
const webdriver = require('selenium-webdriver')
const WDE = require('wd-elements')(webdriver)

const driver = new webdriver.Builder().forBrowser('chrome').build();
const page = WDE.Page.create(driver)

// for the most part WDElements behave like native selenium WebElements
// the true power comes from primitives and extensibility (see advanced usage)
page.find('#app')
.then((app) => app.find('h1'))
.then((h1) => h1.getText())
```

## License

The MIT License (MIT) Copyright (c) 2016 Jarid Margolin

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 changes: 41 additions & 0 deletions lib/index.js
@@ -0,0 +1,41 @@
'use strict'

/* -----------------------------------------------------------------------------
* dependencies
* -------------------------------------------------------------------------- */

// 3rd party
const _ = require('lodash')

// lib
const Primitives = require('./primitives')
const El = require('./primitives/el')

/* -----------------------------------------------------------------------------
* WDElements
* -------------------------------------------------------------------------- */

// add all of our base internal primitives
const primitives = new Primitives()

// config method - lets you set the webdriver module to use - If not specified,
// will utilize module version packaged with WDElement (using mismatched
// versions may have undesired side effects). Setting the driver is recommended
module.exports = function (wd) {
El.configure(wd || require('selenium-webdriver'), primitives)
primitives.load('./primitives')

// Return proxy of WDElemts that implements a dynamic getter to return
// an interface to interact with primitives
return new Proxy({}, {
get: function (target, name) {
if (_.isUndefined(primitives[name])) {
return primitives.get(name)
}

return _.isFunction(primitives[name])
? primitives[name].bind(primitives)
: primitives[name]
}
})
}
91 changes: 91 additions & 0 deletions lib/primitives.js
@@ -0,0 +1,91 @@
'use strict'

/* -----------------------------------------------------------------------------
* dependencies
* -------------------------------------------------------------------------- */

// core
const fs = require('fs')
const path = require('path')

// 3rd party
const _ = require('lodash')
const Promise = require('bluebird')
const callsites = require('callsites')
const requireDir = require('require-directory')

/* -----------------------------------------------------------------------------
* Primitives
* -------------------------------------------------------------------------- */

module.exports = class Primitives extends Map {

set (name, Class) {
let depth = 0
let CurClass = Class

// we know `El` will is our root element so we can explicitly search for it
while (CurClass.name !== 'El') {
CurClass = Object.getPrototypeOf(CurClass)
depth++
}

this.levels = this.levels || []
this.levels[depth] = this.levels[depth] || []
this.levels[depth].push(Class)

return super.set.call(this, name, Class)
}

load (filePath) {
if (!filePath.startsWith('/')) {
filePath = path.join(this._getCallerDir(), filePath)
}

if (!filePath.startsWith('/')) {
filePath = path.join(process.cwd(), filePath)
}

const stat = fs.statSync(filePath)

return stat.isDirectory()
? this._loadDir(filePath)
: this._loadFile(filePath)
}

findByEl (el) {
const Classes = _.flatten(this.levels.reverse())
const results = Promise.mapSeries(Classes, (Class) => {
return Class.matches(el).then((isMatch) => isMatch ? Class : false)
})

return results.then((results) => _.find(results))
}

/* ---------------------------------------------------------------------------
* utils
* ------------------------------------------------------------------------ */

_getCallerDir () {
const c = callsites()

let fileName = c[0].getFileName()
for (let i = 1; i < c.length && fileName.includes('lib/primitives.js'); i++) {
fileName = c[i].getFileName()
}

return path.dirname(fileName)
}

_loadDir (dirPath) {
_.each(requireDir(module, dirPath), (Class) => {
this.set(Class.name, Class)
})
}

_loadFile (filePath) {
const Class = require(filePath)
this.set(Class.name, Class)
}

}

0 comments on commit 594bd47

Please sign in to comment.