Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

browser-monkey

Package Overview
Dependencies
Maintainers
2
Versions
87
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

browser-monkey - npm Package Compare versions

Comparing version 1.29.2 to 2.0.0

create.js

87

actions.js
var debug = require('debug')('browser-monkey');
var dispatchEvent = require('./dispatchEvent');
var sendkeys = require('./sendkeys');
var sendclick = require('./sendclick');
var Options = require('./options');
var $ = require('jquery');
var errorHandler = require('./errorHandler');
function notSillyBlankIEObject(element){
return Object.keys(element).length > 0;
}
module.exports = {
focus: function(element, options) {
var focus = typeof options == 'object' && options.hasOwnProperty('focus')? options.focus: true;
function blurActiveElement() {
var activeElement;
try {
activeElement = document.activeElement;
} catch ( err ) { }
if (focus) {
var $ = this.get('$');
var document = this.get('document');
if (element && element.length > 0) {
element = element[0];
}
if (activeElement) {
dispatchEvent(activeElement, 'blur');
}
}
var activeElement = document.activeElement;
if (activeElement && !$(activeElement).is(':focus') && notSillyBlankIEObject(activeElement)) {
$(activeElement).trigger('blur');
}
document.activeElement = element;
$(element).focus();
}
},
module.exports = {
click: function(options) {
var self = this;
return this.enabled().element(options).then(function(element) {
if (typeof options === 'string') {
self = this.linkOrButton(options);
}
return self.enabled().element(options).then(function(element) {
debug('click', element);
self.handleEvent({type: 'click', element: element});
blurActiveElement();
return sendclick(element);
});
self.focus(element, options);
element.trigger('mousedown');
element.trigger('mouseup');
element.trigger('click');
}).catch(errorHandler(new Error()));
},
select: function(options) {
var selectOptions = Options.remove(options, ['text', 'exactText']);
var $ = this.get('$');
var self = this;
return self.is('select').find('option', selectOptions).elements(options).then(function(optionElements) {
var optionElement = optionElements[0];
optionElement.selected = true;
var selectElement = optionElement.parentNode;
return this.is('select').find('option', options).elements(options).then(function(optionElements) {
var optionElement = $(optionElements[0]);
var selectElement = optionElement.parent();
self.focus(selectElement, options);
optionElement.prop('selected', true);
optionElement.attr('selected', 'selected');
selectElement.val(optionElement.val());

@@ -43,3 +59,3 @@ debug('select', selectElement);

type: 'select option',
value: optionElement.value,
value: selectElement.val(),
element: selectElement,

@@ -49,5 +65,4 @@ optionElement: optionElement

blurActiveElement();
dispatchEvent(selectElement, 'change');
});
selectElement.trigger('change');
}).catch(errorHandler(new Error()));
},

@@ -64,6 +79,6 @@

assertCanTypeIntoElement(element);
self.focus(element, options);
self.handleEvent({type: 'typing', text: text, element: element});
blurActiveElement();
return sendkeys(element, text);
});
}).catch(errorHandler(new Error()));
},

@@ -76,6 +91,6 @@

debug('submit', element);
self.focus(element, options);
self.handleEvent({type: 'submit', element: element});
blurActiveElement();
return $(element).submit();
});
return element.trigger('submit');
}).catch(errorHandler(new Error()));
},

@@ -87,6 +102,7 @@

return this.element(options).then(function(element) {
self.focus(element, options);
debug('typeInHtml', element, html);
self.handleEvent({type: 'typing html', html: html, element: element});
return sendkeys.html(element, html);
});
}).catch(errorHandler(new Error()));
},

@@ -160,3 +176,3 @@

function canTypeIntoElement(element) {
return $(element).is('input:not([type]), ' +
return element.is('input:not([type]), ' +
'input[type=text], ' +

@@ -168,2 +184,3 @@ 'input[type=email], ' +

'input[type=url], ' +
'input[type=number],' +
'textarea');

@@ -174,4 +191,4 @@ }

if (!canTypeIntoElement(element)) {
throw new Error('Cannot type into ' + element.tagName);
throw new Error('Cannot type into ' + element.prop('tagName'));
}
}
var Options = require('./options');
var elementTester = require('./elementTester');
var expectOneElement = require('./expectOneElement');
var errorHandler = require('./errorHandler');
module.exports = {
is: function (css) {
return this.addFinder(elementTester({css: css}));
var $ = this.get('$');
return this.addFinder(this.createElementTester({css: css}));
},

@@ -15,7 +16,10 @@

shouldExist: function (options) {
return this.resolve(options).then(function () {});
var error = new Error();
return this.resolve(options)
.catch(errorHandler(new Error()));
},
shouldNotExist: function (options) {
return this.notResolve(options);
return this.notResolve(options)
.catch(errorHandler(new Error()));
},

@@ -28,2 +32,3 @@

shouldHave: function(options) {
var $ = this.get('$');
var self = this;

@@ -41,10 +46,14 @@

var assertions = additionalAssertions.map(function(finderMethodName){
return self[finderMethodName]().shouldHave(additionalOptions[finderMethodName]);
if (typeof self[finderMethodName] === 'function') {
return self[finderMethodName]().shouldHave(additionalOptions[finderMethodName]);
}
});
assertions.push(this.addFinder(elementTester(options)).shouldExist(resolveOptions));
return Promise.all(assertions);
assertions.push(this.addFinder(this.createElementTester(options)).shouldExist(resolveOptions));
return Promise.all(assertions)
.catch(errorHandler(new Error()));
},
shouldHaveElement: function(fn, options) {
var $ = this.get('$');
var self = this;

@@ -55,3 +64,5 @@

expectOneElement(self, elements);
elements.toArray().forEach(fn);
elements.toArray().forEach(function(element){
fn($(element));
});
return elements;

@@ -74,7 +85,8 @@ }

shouldNotHave: function(options) {
var $ = this.get('$');
var resolveOptions = Options.remove(options, ['timeout', 'interval']);
resolveOptions.allowMultiple = true;
return this.addFinder(elementTester(options)).shouldNotExist(resolveOptions);
return this.addFinder(this.createElementTester(options)).shouldNotExist(resolveOptions);
}
};
module.exports = function elementsToString(els) {
return els.toArray().map(function (el) {
if (el && el.outerHTML)
return el.outerHTML.replace(el.innerHTML, '');
}).join(', ');
}
var chai = require('chai');
var expect = chai.expect;
var $ = require('jquery');
var Options = require('./options');
var elementsToString = require('./elementsToString');
function assertElementProperties(elements, expected, getProperty, exact) {
function assertElementProperties($, elements, expected, getProperty, exact) {
function assertion(actual, expected) {

@@ -24,8 +21,31 @@ if (exact) {

var comparer = exact == true ?
function(a, b) { return a == b; } :
function(a, b) { return a.indexOf(b) != -1}
var found = [];
var actuals = actualTexts.slice();
expected.forEach(function (expected, index) {
var actualText = actualTexts[index];
assertion(actualText, expected);
var actualFound;
for (var actualIndex=0; actualIndex<actualTexts.length; actualIndex++) {
var actual = actualTexts[actualIndex];
if (comparer(actual, expected)) {
actualFound = actual;
found[actualIndex] = expected;
break;
}
}
});
try {
expect(found).to.eql(expected)
} catch(e) {
if (found.slice().sort().toString() == expected.slice().sort().toString()) {
e.message += '\nThe text was found but in a different order than specified - maybe you need some sorting?';
}
throw e;
}
} else {
var elementText = getProperty(elements);
var elementText = getProperty($(elements));
assertion(elementText, expected);

@@ -35,105 +55,101 @@ }

function getNormalisedText(el) {
return el.innerText().replace(/ +/g,' ').replace(/ *\r?\n */g,"\n");
}
module.exports = function elementTester(options) {
var options = new Options(options);
function getValue(e, property) {
var val = e.val();
// Fails with missing value attribute in VDOM without 'string' test (returns SoftSetHook{value: ''})
return (typeof val === "string" && val) || '';
}
var css = options.option('css');
var text = options.option('text');
var exactText = options.option('exactText');
var message = options.option('message');
var predicate = options.option('elements');
var length = options.option('length');
var value = options.option('value');
var exactValue = options.option('exactValue');
var html = options.option('html');
var checked = options.option('checked');
var attributes = options.option('attributes');
module.exports = {
css: function($el, message, css) {
if (!$el.is(css)) {
throw new Error(message || ('expected elements ' + elementsToString($el) + ' to have css ' + css));
}
},
elements: function($el, message, predicate){
if (!predicate($el.toArray())) {
throw new Error(message || 'expected elements to pass predicate');
}
},
text: function($el, message, text) {
assertElementProperties(this.get('$'), $el, text, function (e) { return getNormalisedText(e)}, text === '');
},
length: function($el, message, length) {
if ($el.length !== length) {
throw new Error(message || ('expected ' + elementsToString($el) + ' to have ' + length + ' elements'));
}
},
html: function($el, message, html) {
assertElementProperties(this.get('$'), $el, html, function (e) { return e.html(); });
},
checked: function($el, message, checked) {
var $ = this.get('$');
var elements = $el.toArray();
options.validate();
if (checked instanceof Array) {
var elementsChecked = elements.map(function (element) {
return !!$(element).prop('checked');
});
expect(elementsChecked, 'expected ' + elementsToString($el) + ' to have checked states ' + JSON.stringify(checked)).to.eql(checked);
} else {
var elementsNotMatching = elements.filter(function (element) {
return $(element).prop('checked') !== checked;
});
expect(elementsNotMatching.length, 'expected ' + elementsToString($el) + ' to be ' + (checked? 'checked': 'unchecked')).to.equal(0);
}
},
exactText: function($el, message, exactText) {
assertElementProperties(this.get('$'), $el, exactText, function (e) { return getNormalisedText(e); }, true);
},
value: function($el, message, value) {
assertElementProperties(this.get('$'), $el, value, getValue, value === '');
},
exactValue: function($el, message, exactValue) {
assertElementProperties(this.get('$'), $el, exactValue, getValue, true);
},
attributes: function($el, message, attributes) {
var $ = this.get('$');
var elements = $el.toArray();
if (typeof options === 'string') {
css = options;
}
elements.forEach(function(el){
Object.keys(attributes).forEach(function(attributeKey){
expect($(el).attr(attributeKey)).to.equal(attributes[attributeKey]);
});
});
},
if (typeof options === 'function') {
predicate = options;
}
function getNormalisedText(el) {
return el[0].innerText.replace(/ +/g,' ').replace(/ *\n */g,"\n");
}
return {
find: function(element) {
var els = $(element);
if (css && !els.is(css)) {
if (!els.is(css)) {
throw new Error(message || ('expected elements ' + elementsToString(els) + ' to have css ' + css));
}
label: function($el, message, label) {
var $ = this.get('$');
var links = $el.toArray().filter(function(el) {
if ($(el).is('a')) {
var anchor = $(el);
var href = anchor.attr('href');
return typeof href !== typeof undefined &&
href !== false &&
(
anchor.text() == label ||
anchor.attr('id') == label ||
anchor.attr('title') == label ||
anchor.find("img").toArray().filter(function(img) {
return $(img).attr('alt') == label;
}).length > 0
);
}
if (text !== undefined) {
assertElementProperties(els, text, function (e) { return getNormalisedText(e); });
if ($(el).is('button, input[type=submit], input[type=button], input[type=reset]')) {
var button = $(el);
return button.attr('id') == label ||
button.text() == label ||
(typeof button.attr('value') == 'string' && button.attr('value').indexOf(label) > -1) ||
(typeof button.attr('title') == 'string' && button.attr('title').indexOf(label) > -1) ||
button.find("img").toArray().filter(function(img) {
return typeof $(img).attr('alt') == 'string' && $(img).attr('alt').indexOf(label) > -1;
}).length > 0
}
});
if (exactText !== undefined) {
assertElementProperties(els, exactText, function (e) { return getNormalisedText(e); }, true);
}
if (value !== undefined) {
assertElementProperties(els, value, function (e) { return e.val() || ''; });
}
if (exactValue !== undefined) {
assertElementProperties(els, exactValue, function (e) { return e.val() || ''; }, true);
}
if (checked) {
var elements = els.toArray();
if (checked instanceof Array) {
var elementsChecked = elements.map(function (element) {
return !!element.checked;
});
expect(elementsChecked, 'expected ' + elementsToString(els) + ' to have checked states ' + JSON.stringify(checked)).to.eql(checked);
} else {
var elementsNotMatching = elements.filter(function (element) { return element.checked != checked; });
expect(elementsNotMatching.length, 'expected ' + elementsToString(els) + ' to be ' + (checked? 'checked': 'unchecked')).to.equal(0);
}
}
if (attributes) {
var elements = els.toArray();
elements.forEach(function(el){
Object.keys(attributes).forEach(function(attributeKey){
expect($(el).attr(attributeKey)).to.equal(attributes[attributeKey]);
});
});
}
if (html) {
assertElementProperties(els, html, function (e) { return e.html(); });
}
if (length !== undefined) {
if (els.length !== length) {
throw new Error(message || ('expected ' + elementsToString(els) + ' to have ' + length + ' elements'));
}
}
if (predicate) {
if (!predicate(els.toArray())) {
throw new Error(message || 'expected elements to pass predicate');
}
}
return els;
},
toString: function() {
return message || css || text;
}
};
}
expect(links.length).to.equal(1, message);
}
};

@@ -7,3 +7,3 @@ var chai = require('chai');

var msg = "expected to find exactly one element: " + scope.printFinders(scope._finders) + ', but found :' + elementsToString(elements);
expect(elements.length, msg).to.equal(1);
expect(elements.size(), msg).to.equal(1);
}
var retry = require('trytryagain');
var trace = require('./trace');
var Options = require('./options');
var elementTester = require('./elementTester');
var expectOneElement = require('./expectOneElement');
var $ = require('jquery');
function filterInvisible(index){
var el = this[index] || this;
var ignoreVisibilityOfTags = ['OPTION'];
if (el && ignoreVisibilityOfTags.indexOf(el.tagName) !== -1) {
el = el.parentNode;
}
return $(el).is(':visible');
}
module.exports = {
elementFinder: function(css) {
var $ = this.get('$');
var self = this;

@@ -25,3 +13,10 @@ return {

if (self.get('visibleOnly')) {
els = els.filter(filterInvisible);
els = els.filter(function(index){
var el = this[index] || this;
var ignoreVisibilityOfTags = ['OPTION'];
if (el && ignoreVisibilityOfTags.indexOf(el.tagName) !== -1) {
el = el.parentNode;
}
return $(el).is(':visible');
});
}

@@ -45,3 +40,35 @@ if (els.length > 0) {

createElementTester: function(criteria) {
var self = this;
if (typeof criteria === 'string') {
criteria = { css: criteria };
}
if (typeof criteria === 'function') {
criteria = { predicate: criteria };
}
return {
find: function($el) {
var message = criteria.message;
Object.keys(criteria).forEach(function(key){
var value = criteria[key];
var tester = self._elementTesters[key];
if (value !== undefined && tester !== undefined) {
tester.call(self, $el, message, value);
}
});
return $el;
},
toString: function(){
return criteria.message || criteria.css || criteria.text;
}
};
},
find: function (selector, options) {
var $ = this.get('$');
var message = JSON.stringify(options);

@@ -51,3 +78,3 @@ var scope = this.addFinder(this.elementFinder(selector));

if (options) {
var tester = elementTester(options);
var tester = this.createElementTester(options);

@@ -67,2 +94,3 @@ return scope.filter(function (element) {

containing: function (selector, options) {
var $ = this.get('$');
var message = options && JSON.stringify(options);

@@ -73,3 +101,4 @@ var findElements = this.elementFinder(selector);

if (options) {
var testElements = elementTester(options);
var tester = this.createElementTester(options);
finder = {

@@ -80,3 +109,3 @@ find: function (elements) {

try {
testElements.find(element);
tester.find(element);
return true;

@@ -103,5 +132,5 @@ } catch (error) {

find: function(elements) {
var els = elements.filter(function() {
var els = Array.prototype.filter.call(elements, function(el) {
try {
return finder.find(this);
return finder.find(el);
} catch (e) {

@@ -113,3 +142,3 @@ return false;

if (els.length > 0) {
return els;
return $(els);
}

@@ -124,2 +153,22 @@ },

linkOrButton: function(label) {
return this.find(
'a, button, input[type=submit], input[type=button], input[type=reset]',
{label: label},
"[linkOrButton: " + label + "]"
);
},
button: function(label) {
return this.find(
'button, input[type=submit], input[type=button], input[type=reset]',
{label: label},
"[button: " + label + "]"
);
},
link: function(label) {
return this.find('a', {label: label}, "[link: " + label + "]");
},
elements: function (options) {

@@ -131,4 +180,5 @@ options = Options.default(options, {allowMultiple: true});

element: function (options) {
var $ = this.get('$');
return this.resolve(options).then(function (elements) {
return elements[0];
return $(elements[0]);
});

@@ -142,4 +192,5 @@ },

findElements: function (options) {
var $ = this.get('$');
var self = this;
var allowMultiple = options && options.hasOwnProperty('allowMultiple')? options.allowMultiple: false;
var allowMultiple = Options.get(options, 'allowMultiple');

@@ -153,3 +204,3 @@ function findWithFinder(el, finderIndex) {

if (!found) {
throw new Error("expected to find: " + self.printFinders(self._finders.slice(0, finderIndex + 1)));
throw new Error("expected to find: " + self.printFinders(self._finders));
}

@@ -164,6 +215,18 @@

function selector() {
if(self._selector instanceof Element && self._selector.tagName == 'IFRAME') {
return self._selector.contentDocument.body;
var selector = self._selector;
if (
selector &&
typeof Element !== 'undefined' &&
selector instanceof Element &&
selector.tagName == 'IFRAME'
) {
return selector.contentDocument.body;
} else if (
selector &&
typeof selector.prop === 'function' &&
selector.prop('tagName') === 'IFRAME'
) {
return selector[0].contentDocument.body;
} else {
return self._selector || 'body';
return selector || 'body';
}

@@ -176,2 +239,3 @@ }

}
return elements.toArray();

@@ -182,4 +246,5 @@ },

var self = this;
var retryOptions = Options.remove(options, ['timeout', 'interval', 'trace']);
var traceOption = retryOptions.hasOwnProperty('trace')? retryOptions.trace: true;
var defaultTimeout = this.get('timeout') || 1000;
var retryOptions = Options.remove(options, ['timeout', 'interval']);
retryOptions.timeout = retryOptions.timeout || defaultTimeout;

@@ -190,7 +255,3 @@ var result = retry(retryOptions, function() {

if (traceOption) {
return trace(result);
} else {
return result;
}
return result;
},

@@ -214,5 +275,8 @@

filter: function (filter, message) {
var $ = this.get('$');
return this.addFinder({
find: function (elements) {
var filteredElements = elements.toArray().filter(filter);
var filteredElements = elements.toArray().filter(function(element){
return filter($(element))
});

@@ -232,3 +296,4 @@ if (filteredElements && filteredElements.length > 0) {

return this.filter(function (element) {
return !((element.tagName == 'BUTTON' || element.tagName == 'INPUT') && element.disabled == true);
var tagName = element.prop('tagName');
return !((tagName == 'BUTTON' || tagName == 'INPUT') && element.prop('disabled'));
}, '[disabled=false]');

@@ -235,0 +300,0 @@ }

@@ -1,83 +0,1 @@

var finders = require('./finders');
var actions = require('./actions');
var assertions = require('./assertions');
function Selector(selector, finders, options) {
this._selector = selector;
this._finders = finders || [];
this._options = options || { visibleOnly: true };
this._handlers = [];
}
Selector.prototype.set = function(options){
var self = this;
Object.keys(options).forEach(function(key){
self._options[key] = options[key];
});
return this;
}
Selector.prototype.get = function(key){
return this._options[key];
}
Selector.prototype.clone = function (extension) {
var clone = new this.constructor();
var self = this;
Object.keys(self).forEach(function (key) {
clone[key] = self[key];
});
Object.keys(extension).forEach(function (key) {
clone[key] = extension[key];
});
return clone;
};
Selector.prototype.on = function (handler) {
this._handlers.push(handler);
return this;
};
Selector.prototype.handleEvent = function () {
var args = arguments;
this._handlers.forEach(function (handler) {
handler.apply(undefined, args);
});
};
Selector.prototype.scope = function (scope) {
if (scope instanceof Selector) {
return this.clone(scope);
} else {
return this.clone({_selector: scope});
}
};
Selector.prototype.extend = function (methods) {
return this.component(methods);
};
Selector.prototype.component = function (methods) {
function Component() {
Selector.apply(this, arguments);
}
Component.prototype = new this.constructor();
Object.keys(methods).forEach(function (method) {
Component.prototype[method] = methods[method];
});
Component.prototype.constructor = Component;
return new Component().scope(this);
};
module.exports = new Selector()
.component(finders)
.component(actions)
.component(assertions);
module.exports = require('./create')();

@@ -34,6 +34,10 @@ // Karma configuration

preprocessors: {
'test/global.js': ['browserify'],
'test/global.js': ['browserify', 'env'],
'test/**/*Spec.js': ['browserify']
},
envPreprocessor: [
'BM_TIMEOUT',
],
browserify: {

@@ -43,6 +47,12 @@ debug: true

client: {
mocha: {
timeout: 0
}
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['mocha'],
reporters: process.env.BROWSERS? ['dots']: ['mocha'],

@@ -69,9 +79,81 @@

// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
browsers: process.env.BROWSERS == 'all'? Object.keys(browsers): ['Chrome'],
browserStack: {
username: process.env.BROWSERSTACK_USER,
accessKey: process.env.BROWSERSTACK_PASSWORD
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
singleRun: false,
customLaunchers: browsers,
browserNoActivityTimeout: 60000
});
};
var browsers = {
'browserstack-firefox': {
base: 'BrowserStack',
browser : 'Firefox',
browser_version : '47.0',
os : 'OS X',
os_version : 'El Capitan',
resolution : '1024x768'
},
'browserstack-safari': {
base: 'BrowserStack',
browser : 'Safari',
browser_version : '9.1',
os : 'OS X',
os_version : 'El Capitan',
resolution : '1024x768'
},
'browserstack-safari-ios': {
base: 'BrowserStack',
device : 'iPhone 6S',
os : 'ios',
os_version : '9.1',
},
'browserstack-chrome': {
base: 'BrowserStack',
browser : 'Chrome',
browser_version : '52.0',
os : 'OS X',
os_version : 'El Capitan',
resolution : '1024x768'
},
'browserstack-ie9': {
base: 'BrowserStack',
browser : 'IE',
browser_version : '9.0',
os : 'Windows',
os_version : '7',
resolution : '1024x768'
},
'browserstack-ie10': {
base: 'BrowserStack',
browser : 'IE',
browser_version : '10.0',
os : 'Windows',
os_version : '8',
resolution : '1024x768'
},
'browserstack-ie11': {
base: 'BrowserStack',
browser : 'IE',
browser_version : '11.0',
os : 'Windows',
os_version : '10',
resolution : '1024x768'
},'browserstack-edge': {
base: 'BrowserStack',
browser : 'Edge',
browser_version : '13.0',
os : 'Windows',
os_version : '10',
resolution : '1024x768'
},
};

@@ -7,2 +7,10 @@ function Options(options){

Options.get = function(options, propertyName){
if (typeof options === 'object') {
var value = options[propertyName];
return value;
}
}
Options.remove = function(options, propertyNames){

@@ -9,0 +17,0 @@ var newOptions = {};

{
"name": "browser-monkey",
"version": "1.29.2",
"version": "2.0.0",
"description": "reliable dom testing",
"main": "index.js",
"scripts": {
"test": "./node_modules/.bin/karma start --browsers Firefox --single-run"
"test": "mocha && karma start --single-run"
},

@@ -12,17 +12,29 @@ "author": "Tim Macfarlane <timmacfarlane@gmail.com>",

"devDependencies": {
"chai-as-promised": "5.1.0",
"karma": "0.12.31",
"karma-browserify": "4.0.0",
"karma-chrome-launcher": "0.1.7",
"karma-cli": "0.0.4",
"karma-firefox-launcher": "^0.1.7",
"karma-mocha": "0.1.10",
"karma-mocha-reporter": "1.0.2",
"2vdom": "^0.2.0",
"browserify": "^13.1.1",
"chai-as-promised": "6.0.0",
"detect-node": "^2.0.3",
"hyperdom": "^0.2.0",
"karma": "1.3.0",
"karma-browserify": "5.1.0",
"karma-browserstack-launcher": "1.1.1",
"karma-chrome-launcher": "2.0.0",
"karma-cli": "1.0.1",
"karma-env-preprocessor": "0.1.1",
"karma-firefox-launcher": "^1.0.0",
"karma-ievms": "0.1.0",
"karma-mocha": "1.2.0",
"karma-mocha-reporter": "2.2.0",
"lie": "^3.0.1",
"mocha": "2.2.1"
"mocha": "3.1.2",
"vdom-query": "https://github.com/featurist/vdom-query",
"virtual-dom": "^2.1.1",
"watchify": "^3.7.0"
},
"dependencies": {
"chai": "3.3.0",
"chai": "3.5.0",
"debug": "2.2.0",
"jquery": "2.1.3",
"detect-browser": "1.5.0",
"global": "^4.3.0",
"jquery": "2.2.4",
"trytryagain": "1.2.0"

@@ -29,0 +41,0 @@ },

@@ -24,18 +24,22 @@ # browser monkey [![npm version](https://img.shields.io/npm/v/browser-monkey.svg)](https://www.npmjs.com/package/browser-monkey) [![npm](https://img.shields.io/npm/dm/browser-monkey.svg)](https://www.npmjs.com/package/browser-monkey) [![Build Status](https://travis-ci.org/featurist/browser-monkey.svg?branch=master)](https://travis-ci.org/featurist/browser-monkey)

```js
import createBrowser from 'browser-monkey/create';
describe('admin', function () {
let browser = createBrowser(document.body);
// describes the admin panel, with a search box, results and a user editor
var adminPanel = browser.component({
searchUsers: function () {
let adminPanel = browser.component({
searchUsers() {
return this.find('.search');
},
userResult: function (name) {
userResult(name) {
// find a user in the results by their name
return this.find('.results .user', {text: name});
}
userEditor: function () {
},
userEditor() {
// return the user editor, scoped to the .user-editor div.
return this.find('.user-editor').component({
name: function () { this.find('.name'); },
email: function () { this.find('.email'); },
save: function () { this.find('.save'); }
name() { return this.find('.name'); },
email() { return this.find('.email'); },
save() { return this.find('.save'); }
});

@@ -45,17 +49,13 @@ }

it('can search for, edit and save a user', function () {
return adminPanel.searchUsers().typeIn('bar').then(function () {
return adminPanel.userResult('Barry').click();
}).then(function () {
var userEditor = adminPanel.userEditor();
return Promise.all([
userEditor.name().typeIn('Barry Jones'),
userEditor.email().typeIn('barryjones@example.com')
]).then(function () {
return userEditor.save().click();
});
}).then(function () {
// verify that the user was saved
// use mock-xhr-router!
});
it('can search for, edit and save a user', async () => {
await adminPanel.searchUsers().typeIn('bar');
await adminPanel.userResult('Barry').click();
let userEditor = adminPanel.userEditor();
await userEditor.name().typeIn('Barry Jones');
await userEditor.email().typeIn('barryjones@example.com');
await userEditor.save().click();
// verify that the user was saved
});

@@ -109,6 +109,7 @@ });

innerScope.get(); // {visibleOnly: false};
innerScope.get('visibleOnly'); // returns false
```
* `visibleOnly` if true, then only visible elements will be found, if false, then all elements are considered. Visible is determined by the element's computed CSS, see [jQuery's :visible selector](https://api.jquery.com/visible-selector/). Default is true.
* `timeout` an integer specifying the milliseconds to wait for an element to appear. This can be overriden by specifying the timeout when calling an action.

@@ -517,1 +518,27 @@ ## find

* `event.html` is the html entered, in the case of type `'typing html'`.
# Semantic Finders
Semantic finders use a text fragment to find matching elements.
## link
`browser.link('gorilla')` matches:
- `<a>gorilla</a>`
- `<a id="gorilla">link</a>`
- `<a title="gorilla">link</a>`
- `<a><img alt="gorilla"></a>`
## button
A button is considered any of the following types - `input[type=submit]`, `input[type=button]`, `input[type=reset]`
`browser.button('tamarin')` matches:
- `<button>tamarin</button>`
- `<button id="tamarin">button</button>`
- `<button value="tamarin">button</button>`
- `<button id="tamarin">button</button>`
- `<button><img alt="tamarin"></button>`
## linkOrButton
would match either a link or button according to their respective rules
## click
Normally the click action is performed on a scope but you can also provide it with a string `browser.click('monkey')` and it will search for a link or button that matches and perform the click on it.
function dispatchEvent(el, type, char) {
var event = document.createEvent("Events");
event.initEvent(type, true, false);
event.charCode = char;
el.dispatchEvent(event);
el.trigger(type, {charCode: char});
}

@@ -10,17 +7,12 @@

dispatchEvent(el, "keydown", char);
dispatchEvent(el, "keyup", char);
dispatchEvent(el, "keypress", char);
dispatchEvent(el, "input");
dispatchEvent(el, "keyup", char);
}
function sendkeys(el, text) {
el.focus();
if (el.setActive) {
el.setActive();
}
var originalValue = el.val();
var originalValue = el.value;
if (text.length === 0) {
el.value = '';
el.val('');
sendkey(el, '');

@@ -30,3 +22,4 @@ } else {

var char = text[n];
el.value = text.substring(0, n + 1);
var value = text.substring(0, n + 1);
el.val(value);
sendkey(el, char);

@@ -33,0 +26,0 @@ }

@@ -1,14 +0,15 @@

var browser = require('..');
var createTestDom = require('./createTestDom');
var $ = require('jquery');
var domTest = require('./domTest');
var retry = require('trytryagain');
describe('actions', function(){
var dom;
describe('clicking', function () {
domTest('stack trace', function(browser, dom){
return browser.find('div')
.click()
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
beforeEach(function(){
dom = createTestDom();
});
describe('clicking', function () {
it('should eventually click an element', function () {
domTest('should eventually click an element', function (browser, dom, $) {
var promise = browser.find('.element').click();

@@ -18,3 +19,3 @@ var clicked = false;

dom.eventuallyInsert(
$('<div class="element"></div>').click(function () {
$('<div class="element"></div>').on('click', function () {
clicked = true;

@@ -29,10 +30,10 @@ })

it('sends mousedown mouseup and click events', function () {
domTest('sends mousedown mouseup and click events', function (browser, dom) {
var events = [];
dom.insert('<div class="element"></div>').mousedown(function () {
dom.insert('<div class="element"></div>').on('mousedown', function () {
events.push('mousedown');
}).mouseup(function () {
}).on('mouseup', function () {
events.push('mouseup');
}).click(function () {
}).on('click', function () {
events.push('click');

@@ -46,3 +47,19 @@ });

it('waits until checkbox is enabled before clicking', function () {
domTest('mousedown mouseup and click events bubble up to parent', function (browser, dom) {
var events = [];
dom.insert('<div class="element"><div class="inner-element">inner</div></div>').on('mousedown', function () {
events.push('mousedown');
}).on('mouseup', function () {
events.push('mouseup');
}).on('click', function () {
events.push('click');
});
return browser.find('.inner-element').click().then(function () {
expect(events).to.eql(['mousedown', 'mouseup', 'click']);
});
}, {vdom: false});
domTest('waits until checkbox is enabled before clicking', function (browser, dom) {
var promise = browser.find('input[type=checkbox]').click();

@@ -53,3 +70,3 @@ var clicked;

var button = dom.insert('<input type=checkbox disabled></input>');
button[0].addEventListener('click', function () {
button.on('click', function () {
clicked = buttonState;

@@ -61,3 +78,3 @@ });

buttonState = 'enabled'
}, 100);
}, 10);

@@ -69,3 +86,3 @@ return promise.then(function () {

it('waits until button is enabled before clicking', function () {
domTest('waits until button is enabled before clicking', function (browser, dom) {
var promise = browser.find('button', {text: 'a button'}).click();

@@ -75,4 +92,4 @@ var clicked;

var button = dom.insert('<button disabled>a button</button>');
button[0].addEventListener('click', function () {
button = dom.insert('<button disabled>a button</button>');
button.on('click', function () {
clicked = buttonState;

@@ -84,3 +101,3 @@ });

buttonState = 'enabled'
}, 100);
}, 10);

@@ -94,17 +111,22 @@ return promise.then(function () {

describe('select', function(){
domTest('stack trace', function(browser, dom){
return browser.find('div')
.select({text: 'Text'})
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
describe('text', function(){
it('respects timeout option', function(){
var promise = browser.find('.element').select({text: 'Second', timeout: 100});
domTest('respects timeout option', function(browser, dom, $){
var promise = browser.find('.element').select({text: 'Second', timeout: 3 });
dom.eventuallyInsert(
$('<select class="element"><option>First</option><option>Second</option></select>').change(function (e) {
var el = e.target;
selectedItem = el.options[el.selectedIndex].text;
})
);
$('<select class="element"><option>First</option><option>Second</option></select>')
, 6);
return expect(promise).to.be.rejected
return expect(promise).to.be.rejected;
});
it('eventually selects an option element using the text', function(){
domTest('should eventually select an option element using the text', function(browser, dom, $){
var promise = browser.find('.element').select({text: 'Second'});

@@ -114,5 +136,4 @@ var selectedItem = undefined;

dom.eventuallyInsert(
$('<select class="element"><option>First</option><option>Second</option></select>').change(function (e) {
var el = e.target;
selectedItem = el.options[el.selectedIndex].text;
$('<select class="element"><option>First</option><option>Second</option></select>').on('change', function () {
selectedItem = $(this).find('option[selected]').text();
})

@@ -126,3 +147,3 @@ );

it('eventually selects an option element using a partial match', function(){
domTest('should eventually select an option element using a partial match', function(browser, dom, $){
var promise = browser.find('.element').select({text: 'Seco'});

@@ -132,5 +153,4 @@ var selectedItem = undefined;

dom.eventuallyInsert(
$('<select class="element"><option>First</option><option>Second</option></select>').change(function (e) {
var el = e.target;
selectedItem = el.options[el.selectedIndex].text;
$('<select class="element"><option>First</option><option>Second</option></select>').on('change', function (e) {
selectedItem = $(this).find('option[selected]').text();
})

@@ -144,6 +164,6 @@ );

it('selects the first match if multiple available', function(){
domTest('selects the first match if multiple available', function(browser, dom, $){
var selectedItem = undefined;
var select = dom.insert('<select><option value="1">Item</option><option value="2">Item</option></select>').change(function (e) {
var select = dom.insert('<select><option value="1">Item</option><option value="2">Item</option></select>').on('change', function (e) {
selectedItem = select.val();

@@ -157,13 +177,12 @@ });

it('selects an option that eventually appears', function(){
domTest('selects an option that eventually appears', function(browser, dom, $){
var promise = browser.find('.element').select({text: 'Second'});
var selectedItem = undefined;
var select = dom.insert('<select class="element"></select>').change(function (e) {
var el = e.target;
selectedItem = el.options[el.selectedIndex].text;
var select = dom.insert('<select class="element"></select>').on('change', function (e) {
selectedItem = $(this).find('option[selected]').text();
});
setTimeout(function () {
$('<option>First</option><option>Second</option>').appendTo(select);
select.append('<option>First</option><option>Second</option>');
}, 20);

@@ -176,3 +195,3 @@

it('errors when the specified option does not exist', function(){
domTest('errors when the specified option does not exist', function(browser, dom){
var promise = browser.find('.element').select({text: 'Does not exist'});

@@ -182,8 +201,6 @@

return Promise.all([
expect(promise).to.be.rejected
]);
return expect(promise).to.be.rejected;
});
it('errors when the input is not a select', function(){
domTest('errors when the input is not a select', function(browser, dom){
var promise = browser.find('.element').select({text: 'Whatevs'});

@@ -194,9 +211,8 @@ dom.eventuallyInsert('<div class="element"></div>');

it('selects an option using text that is falsy', function(){
domTest('selects an option using text that is falsy', function(browser, dom, $){
var promise = browser.find('.element').select({text: 0});
var selectedItem = undefined;
var select = dom.insert('<select class="element"><option>0</option><option>1</option></select>').change(function (e) {
var el = e.currentTarget;
selectedItem = el.options[el.selectedIndex].text;
var select = dom.insert('<select class="element"><option>0</option><option>1</option></select>').on('change', function (e) {
selectedItem = $(this).find('option[selected]').text();
});

@@ -212,9 +228,8 @@

describe('exactText', function(){
it('should select an option using exact text that would otherwise match multiple options', function(){
domTest('should select an option using exact text that would otherwise match multiple options', function(browser, dom, $){
var promise = browser.find('.element').select({exactText: 'Mr'});
var selectedItem = undefined;
var select = dom.insert('<select class="element"><option>Mr</option><option>Mrs</option></select>').change(function (e) {
var el = e.currentTarget;
selectedItem = el.options[el.selectedIndex].text;
var select = dom.insert('<select class="element"><option>Mr</option><option>Mrs</option></select>').on('change', function (e) {
selectedItem = $(this).find('option[selected]').text();
});

@@ -228,9 +243,8 @@

it('should select an option using exact text that is falsy', function(){
domTest('should select an option using exact text that is falsy', function(browser, dom, $){
var promise = browser.find('.element').select({exactText: 0});
var selectedItem = undefined;
var select = dom.insert('<select class="element"><option>0</option><option>1</option></select>').change(function (e) {
var el = e.currentTarget;
selectedItem = el.options[el.selectedIndex].text;
var select = dom.insert('<select class="element"><option>0</option><option>1</option></select>').on('change', function (e) {
selectedItem = $(this).find('option[selected]').text();
});

@@ -247,9 +261,30 @@

describe('submit', function () {
it('should submit the form', function () {
var submitted;
domTest('stack trace', function(browser, dom){
return browser.find('div')
.submit()
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
domTest('should submit the form', function (browser, dom) {
var submitted = false;
var promise = browser.find('input').submit();
dom.insert('<form><input type=text></form>').submit(function (ev) {
dom.insert('<form action="#"><input type=text></form>').on('submit', function (ev) {
submitted = true;
});
return promise.then(function () {
expect(submitted).to.be.true;
});
});
domTest('should submit the form when submit button is clicked', function (browser, dom) {
var submitted = false;
var promise = browser.find('input').click();
dom.insert('<form action="#"><input type="submit">submit</input></form>').on('submit', function (ev) {
ev.preventDefault();
submitted = true;
});

@@ -260,6 +295,14 @@

});
});
}, {vdom: false});
});
describe('typeIn', function(){
domTest('stack trace', function(browser, dom){
return browser.find('input')
.typeIn('hello')
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
[

@@ -273,15 +316,15 @@ '<input class="element"></input>',

'<input class="element" type="url"></input>',
'<input class="element" type="number"></input>',
'<textarea class="element"></textara>'
].forEach(function(html) {
it('eventually enters text into: ' + html, function () {
var promise = browser.find('.element').typeIn('haha');
domTest('eventually enters text into: ' + html, function (browser, dom) {
var promise = browser.find('.element').typeIn('1234');
dom.eventuallyInsert(html);
return promise.then(function () {
expect(dom.el.find('.element').val()).to.equal('haha');
expect(dom.el.find('.element').val()).to.equal('1234');
});
});
});
return
[

@@ -292,12 +335,11 @@ '<div class="element"></div>',

].forEach(function(html) {
it('rejects attempt to type into element: ' + html, function () {
domTest('rejects attempt to type into element: ' + html, function (browser, dom, $) {
var promise = browser.find('.element').typeIn('whatevs');
dom.eventuallyInsert(html);
return expect(promise).to.be.rejectedWith('Cannot type into ' + $(html)[0].tagName);
return expect(promise).to.be.rejectedWith('Cannot type into ' + $(html).prop('tagName'));
});
});
it('blanks out existing text when typing empty text', function () {
domTest('blanks out existing text when typing empty text', function (browser, dom) {
var firedEvents = [];

@@ -308,12 +350,28 @@ dom.insert('<input type="text" class="element" value="good bye">')

return browser.find('.element').typeIn('').then(function () {
expect(dom.el.find('input.element').val()).to.equal('');
expect(firedEvents).to.eql(['input: ""'])
return retry(function(){
expect(dom.el.find('input.element').val()).to.equal('');
expect(firedEvents).to.eql(['input']);
});
});
});
});
describe('typeInHtml', function(){
domTest('stack trace', function(browser, dom){
return browser.find('input')
.typeInHtml('<p>hello</p>')
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
});
describe('checkboxes', function(){
it('can check a checkbox by clicking it', function () {
domTest('can check a checkbox by clicking on it', function (browser, dom, $) {
var checkbox = dom.insert('<input class="checkbox" type=checkbox>');
var checked;
checkbox.on('click', function (ev) {
checked = $(this).prop('checked');
});
expect(checkbox.prop('checked')).to.be.false;

@@ -324,2 +382,3 @@

expect(checkbox.prop('checked')).to.be.true;
expect(checked).to.be.true;
}).then(function () {

@@ -329,8 +388,32 @@ return browser.find('.checkbox').click();

expect(checkbox.prop('checked')).to.be.false;
expect(checked).to.be.false;
});
});
domTest('can check a checkbox by clicking its label', function (browser, dom, $) {
var label = dom.insert('<label>Check: <input class="checkbox" type=checkbox></label>');
var checkbox = dom.el.find('input');
var checked;
checkbox.on('click', function (ev) {
checked = $(this).prop('checked');
});
expect(checkbox.prop('checked')).to.be.false;
var clicked = browser.find('label').click();
return clicked.then(function () {
expect(checkbox.prop('checked')).to.be.true;
expect(checked).to.be.true;
}).then(function () {
return browser.find('.checkbox').click();
}).then(function () {
expect(checkbox.prop('checked')).to.be.false;
expect(checked).to.be.false;
});
});
});
describe('fill', function(){
it('fills a component with the supplied values', function(){
domTest('fills a component with the supplied values', function(browser, dom){
var component = browser.component({

@@ -344,3 +427,4 @@ title: function(){

});
dom.eventuallyInsert('<select class="title"><option>Mrs</option><option>Mr</option></select><input type="text" class="name"></input>');
dom.eventuallyInsert('<select class="title"><option>Mrs</option><option>Mr</option></select>');
dom.eventuallyInsert('<input type="text" class="name"></input>');

@@ -356,3 +440,3 @@ return component.fill([

it('can fill using shortcut syntax', function(){
domTest('can fill using shortcut syntax', function(browser, dom){
var component = browser.component({

@@ -369,3 +453,5 @@ title: function(){

});
dom.eventuallyInsert('<select class="title"><option>Mrs</option><option>Mr</option></select><input type="text" class="name"></input><label class="agree"><input type="checkbox"></label>');
dom.eventuallyInsert('<select class="title"><option>Mrs</option><option>Mr</option></select>');
dom.eventuallyInsert('<input type="text" class="name"></input>');
dom.eventuallyInsert('<label class="agree">Check: <input type="checkbox"></label>');

@@ -383,3 +469,3 @@ return component.fill([

it('can execute actions on a component', function(){
domTest('can execute actions on a component', function(browser, dom){
var myActionRan = false;

@@ -408,3 +494,3 @@ var component = browser.component({

it('throws an error if the action cannot be found', function(){
domTest('throws an error if the action cannot be found', function(browser, dom){
var component = browser.component({});

@@ -420,3 +506,3 @@ var error;

it('throws an error when trying to call an action on a field which does not exist', function(){
domTest('throws an error when trying to call an action on a field which does not exist', function(browser, dom){
var component = browser.component({});

@@ -431,3 +517,3 @@

it('throws an error if the field does not exist', function(){
domTest('throws an error if the field does not exist', function(browser, dom){
var component = browser.component({});

@@ -442,2 +528,2 @@

});
});
})

@@ -1,15 +0,9 @@

var browser = require('..');
var createTestDom = require('./createTestDom');
var domTest = require('./domTest');
describe('assertions', function(){
var dom;
beforeEach(function(){
dom = createTestDom();
});
describe('shouldNotExist', function () {
it("should ensure that element eventually doesn't exists", function () {
var elementToRemove = dom.insert('<div class="removing"></div>');
var elementToStay = dom.insert('<div class="staying"></div>');
domTest("should ensure that element eventually doesn't exists", function (browser, dom) {
dom.insert('<div class="removing"></div>');
dom.insert('<div class="staying"></div>');

@@ -20,3 +14,3 @@ var good = browser.find('.removing').shouldNotExist();

setTimeout(function () {
elementToRemove.remove();
dom.el.find('.removing').remove();
}, 50);

@@ -30,4 +24,4 @@

it('allows trytryagain parameters to be used', function () {
var elementToRemove = dom.insert('<div class="removing"></div>');
domTest('allows trytryagain parameters to be used', function (browser, dom) {
dom.insert('<div class="removing"></div>');

@@ -37,3 +31,3 @@ var promise = browser.find('.removing').shouldNotExist({timeout: 500, interval: 100});

setTimeout(function () {
elementToRemove.remove();
dom.el.find('.removing').remove();
}, 50);

@@ -43,6 +37,26 @@

});
domTest('stack trace', function(browser, dom){
dom.insert('<div></div>');
return browser.find('div')
.shouldNotExist()
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
});
describe('shouldExist', function(){
domTest('stack trace', function(browser, dom){
return browser.find('div')
.shouldExist()
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
});
describe('is', function () {
it('should eventually find an element if it has a class', function () {
domTest('should eventually find an element if it has a class', function (browser, dom) {
var good = browser.find('.element').is('.good').shouldExist();

@@ -56,4 +70,4 @@ var bad = browser.find('.element').is('.bad').shouldExist();

element.addClass('good');
}, 100);
}, 200);
}, 10);
}, 10);

@@ -63,3 +77,4 @@ return Promise.all([good, expect(bad).to.be.rejected]);

});
it('eventually finds an element containing text', function () {
domTest('eventually finds an element containing text', function (browser, dom) {
var promise = browser.find('.element', {text: 'some t'}).shouldExist();

@@ -70,4 +85,4 @@ dom.eventuallyInsert('<div class="element"><div>some text</div></div>');

it('eventually finds an element containing text as it appears on the page', function () {
var promise = browser.find('.element', {text: 'This is some text that is all on one line.\nAnd some more on another line'}).shouldExist();
domTest('eventually finds an element containing text as it appears on the page', function (browser, dom) {
var promise = browser.find('.element').shouldHave({text: 'This is some text that is all on one line.\nAnd some more on another line'});
dom.eventuallyInsert('<div class="element"><div>\

@@ -85,3 +100,3 @@ This\

it('eventually finds an element containing exactText', function () {
domTest('eventually finds an element containing exactText', function (browser, dom) {
var good = browser.find('.a', {exactText: '8'}).shouldExist();

@@ -98,4 +113,25 @@ var bad = browser.find('.b', {exactText: '8'}).shouldExist();

domTest("treats assertion of text: '' as exact text", function (browser, dom) {
dom.eventuallyInsert('<div><div class="a">something</div><div class="b"></div></div>');
return browser.find('.a', {text: 'something'}).shouldExist().then(function () {
return Promise.all([
browser.find('.a', {text: ''}).shouldNotExist(),
browser.find('.b', {text: ''}).shouldExist()
])
})
});
describe('shouldHave', function () {
it('eventually finds an element and asserts that it has text', function () {
domTest('stack trace', function(browser, dom){
dom.insert('<div>hello</div>');
return browser.find('div')
.shouldHave({text: 'something'})
.assertStackTrace(__filename);
}, {
mochaOnly: true
});
domTest('eventually finds an element and asserts that it has text', function (browser, dom) {
var good = browser.find('.element').shouldHave({text: 'some t'});

@@ -112,3 +148,3 @@ var bad = browser.find('.element').shouldHave({text: 'sme t'});

it('allows trytryagain parameters to be used', function () {
domTest('allows trytryagain parameters to be used', function (browser, dom) {
var good = browser.find('.element').shouldHave({text: 'some t', timeout: 400, interval: 100});

@@ -125,3 +161,3 @@ var bad = browser.find('.element').shouldHave({text: 'sme t'});

it('eventually finds an element and asserts that it has value', function () {
domTest('eventually finds an element and asserts that it has value', function (browser, dom) {
var good1 = browser.find('.element1 input').shouldHave({value: 'some t'});

@@ -141,3 +177,19 @@ var good2 = browser.find('.element2 input').shouldHave({value: 0});

it('finds an element with exact value', function () {
domTest('error has a tip with suggestions for how to fix it', function(browser, dom){
dom.insert('<span>abc</span>');
dom.insert('<span>bac</span>');
dom.insert('<span>cba</span>');
return browser.find('span').shouldHave({
text: [
'cba',
'abc',
'bac',
]
}).catch(function(error) {
expect(error.message).to.include('\nThe text was found but in a different order than specified - maybe you need some sorting?');
});
});
domTest('finds an element with exact value', function (browser, dom) {
var bad = browser.find('.element1 input').shouldHave({exactValue: 'some t'});

@@ -154,3 +206,18 @@ var good = browser.find('.element1 input').shouldHave({exactValue: 'some text'});

it("treats selects with no value as empty string", function(){
domTest("treats assertion of value: '' as exact value", function (browser, dom) {
var bad = browser.find('.element1 input').shouldHave({value: ''});
var good = browser.find('.element2 input').shouldHave({value: ''});
dom.eventuallyInsert('<div>\n' +
'<div class="element1"><input type=text value="some text" /></div>\n' +
'<div class="element2"><input type=text value="" /></div>\n' +
'</div>');
return Promise.all([
good,
expect(bad).to.be.rejected
]);
});
domTest("treats selects with no value as empty string", function(browser, dom){
dom.insert('<select></select>');

@@ -166,3 +233,3 @@

it('recurses through a tree of assertions', function(){
domTest('recurses through a tree of assertions', function(browser, dom){
dom.insert('<div class="airport"><span class="date">Aug 2055</span><span class="text">LHR</span><span class="blank"></span></div>');

@@ -186,3 +253,3 @@ return browser.component({

it('verifies attributes are present', function(){
domTest('verifies attributes are present', function(browser, dom){
dom.insert('<a id="abc" href="/home">hello</a>');

@@ -201,3 +268,3 @@ var good = browser.find('a').shouldHave({

return Promise.all([
good,
good,
expect(bad).to.be.rejected

@@ -208,3 +275,3 @@ ]);

describe('exactText', function(){
it('eventually finds elements that have the exact array of text', function(){
domTest('eventually finds elements that have the exact array of text', function(browser, dom){
var promise = browser.find('.element option').shouldHave({exactText: ['', 'Mr', 'Mrs']});

@@ -216,6 +283,14 @@

});
domTest('fails to find exact text', function(browser, dom){
var promise = browser.find('option').shouldHave({exactText: ['', 'Mr', 'Mrs']});
dom.eventuallyInsert('<select><option>Optional</option><option>Mr</option><option>Mrs</option></select>');
return expect(promise).to.be.rejected;
});
});
describe('checkboxes', function () {
it('eventually finds a checked checkbox', function () {
domTest('eventually finds a checked checkbox', function (browser, dom) {
var good = browser.find('.checkbox').shouldHave({checked: true});

@@ -233,9 +308,9 @@

it('fails if only one of many checkboxes is checked', function () {
domTest('fails if only one of many checkboxes is checked', function (browser, dom) {
var good = browser.find('.checkbox').shouldHave({checked: true});
var checkbox = dom.insert('<input class="checkbox" type=checkbox /><input class="checkbox" type=checkbox />');
var checkbox = dom.insert('<input class="checkbox" type=checkbox />');
dom.insert('<input class="checkbox" type=checkbox />');
setTimeout(function () {
checkbox[0].checked = true;
checkbox.prop('checked', true);
}, 20);

@@ -248,9 +323,10 @@

it('ensures that each checkbox in the scope is either checked or unchecked', function () {
domTest('ensures that each checkbox in the scope is either checked or unchecked', function (browser, dom) {
var good = browser.find('.checkbox').shouldHave({checked: [true, false]});
var bad = browser.find('.checkbox').shouldHave({checked: [false, true]});
var checkbox = dom.insert('<input class="checkbox" type=checkbox /><input class="checkbox" type=checkbox />');
var checkbox = dom.insert('<input class="checkbox" type=checkbox />');
dom.insert('<input class="checkbox" type=checkbox />');
setTimeout(function () {
checkbox[0].checked = true;
checkbox.prop('checked', true);
}, 20);

@@ -264,3 +340,3 @@

it('fails to find a checked checkbox', function () {
domTest('fails to find a checked checkbox', function (browser, dom) {
var good = browser.find('.checkbox').shouldHave({checked: false});

@@ -278,3 +354,3 @@ var bad = browser.find('.checkbox').shouldHave({checked: true});

it('eventually finds elements and asserts that they each have text', function () {
domTest('eventually finds elements and asserts that they each have text', function (browser, dom) {
var good = browser.find('.element div').shouldHave({text: ['one', 2]});

@@ -293,3 +369,3 @@ var bad1 = browser.find('.element div').shouldHave({text: ['one']});

it('eventually finds elements and asserts that they each have value', function () {
domTest('eventually finds elements and asserts that they each have value', function (browser, dom) {
var good = browser.find('.element input').shouldHave({value: ['one', 2, 0]});

@@ -308,3 +384,3 @@ var bad1 = browser.find('.element input').shouldHave({value: ['one']});

it('eventually finds an element and asserts that it has css', function () {
domTest('eventually finds an element and asserts that it has css', function (browser, dom) {
var good = browser.find('.element').shouldHave({css: '.the-class'});

@@ -323,7 +399,8 @@ var bad1 = browser.find('.element').shouldHave({css: '.not-the-class'});

it('eventually finds an element and asserts that it has n elements', function () {
domTest('eventually finds an element and asserts that it has n elements', function (browser, dom) {
var good = browser.find('.element').shouldHave({length: 2});
var bad1 = browser.find('.element').shouldHave({length: 1});
dom.eventuallyInsert('<div class="element"></div><div class="element"></div>');
dom.eventuallyInsert('<div class="element"></div>');
dom.eventuallyInsert('<div class="element"></div>');

@@ -336,17 +413,18 @@ return Promise.all([

it('eventually finds an element and asserts that it passes an assertion', function () {
domTest('eventually finds an element and asserts that it passes an assertion', function (browser, dom, $) {
var good1 = browser.find('.element').shouldHaveElement(function (element) {
expect(element.innerText).to.equal('a');
expect(element.text()).to.equal('a');
});
var bad1 = browser.find('.multi').shouldHaveElement(function (element) {
expect(element.innerText).to.equal('b');
expect(element.text()).to.equal('b');
});
var bad2 = browser.find('.element').shouldHaveElement(function (element) {
expect(element.innerText).to.equal('b');
expect(element.text()).to.equal('b');
});
var element = dom.insert('<div class="element"></div>');
dom.eventuallyInsert('<div class="multi"></div><div class="multi">b</div>');
dom.eventuallyInsert('<div class="multi"></div>');
dom.eventuallyInsert('<div class="multi">b</div>');

@@ -364,6 +442,6 @@ setTimeout(function () {

it('eventually finds elements and asserts that they pass an assertion', function () {
domTest('eventually finds elements and asserts that they pass an assertion', function (browser, dom, $) {
var good1 = browser.find('.element').shouldHaveElements(function (elements) {
var xs = elements.map(function (element) {
return element.dataset.x;
return $(element).attr('data-x');
});

@@ -376,3 +454,3 @@

var xs = elements.map(function (element) {
return element.dataset.x;
return $(element).attr('data-x');
});

@@ -383,3 +461,5 @@

dom.eventuallyInsert('<div class="element" data-x="one"></div><div class="element" data-x="two"></div><div class="element" data-x="three"></div>');
dom.eventuallyInsert('<div class="element" data-x="one"></div>');
dom.eventuallyInsert('<div class="element" data-x="two"></div>');
dom.eventuallyInsert('<div class="element" data-x="three"></div>');

@@ -394,3 +474,3 @@ return Promise.all([

describe('shouldNotHave', function () {
it('eventually finds an element and asserts that it does not have text', function () {
domTest('eventually finds an element and asserts that it does not have text', function (browser, dom) {
var promise = browser.find('.element').shouldNotHave({text: 'sme t'});

@@ -403,3 +483,3 @@

it('allows trytryagain parameters to be used', function () {
domTest('allows trytryagain parameters to be used', function (browser, dom) {
var promise = browser.find('.element').shouldNotHave({text: 'sme t', timeout: 400, interval: 100});

@@ -406,0 +486,0 @@

@@ -1,12 +0,5 @@

var browser = require('..');
var createTestDom = require('./createTestDom');
var domTest = require('./domTest');
describe('component', function () {
var dom;
beforeEach(function(){
dom = createTestDom();
});
it('can return new selectors by extending', function () {
domTest('can return new selectors by extending', function (browser, dom) {
var user = browser.component({

@@ -29,3 +22,3 @@ name: function () {

it('components are independent', function () {
domTest('components are independent', function (browser, dom) {
var user = browser.component({

@@ -50,3 +43,3 @@ name: function () {

it('can extend another component', function () {
domTest('can extend another component', function (browser, dom) {
var user = browser.component({

@@ -76,3 +69,3 @@ name: function () {

it('can return new scoped selectors', function () {
domTest('can return new scoped selectors', function (browser, dom) {
var admin = browser.component({

@@ -101,3 +94,3 @@ user: function () {

it('components inherit scope', function () {
domTest('components inherit scope', function (browser, dom) {
var adminArea = browser.find('.admin');

@@ -104,0 +97,0 @@

@@ -24,9 +24,9 @@ var $ = require('jquery');

},
eventuallyInsert: function(html) {
eventuallyInsert: function(html, after) {
var self = this;
setTimeout(function () {
self.insert(html);
}, 200);
}, after || 10);
}
};
};

@@ -1,12 +0,6 @@

var browser = require('..');
var createTestDom = require('./createTestDom');
var retry = require('trytryagain');
var domTest = require('./domTest');
describe('events', function(){
var dom;
beforeEach(function(){
dom = createTestDom();
});
it('typeIn element should fire change', function(){
domTest('typeIn element should fire change', function(browser, dom){
var firedEvents = [];

@@ -28,3 +22,3 @@

it('typeIn element should fire input on each character', function(){
domTest('typeIn element should fire input on each character', function(browser, dom){
var firedEvents = [];

@@ -46,10 +40,11 @@

it('typeIn element should fire change and then blur event on input', function(){
domTest('typeIn element should fire change and then blur event on input', function(browser, dom){
var firedEvents = [];
dom.insert('<input type="text" class="input"><input type="text" class="change">');
dom.insert('<input type="text" class="input" />');
dom.insert('<input type="text" class="change" />');
dom.el.find('.input').one('blur', function(e){
dom.el.find('.input').on('blur', function(e){
firedEvents.push('blur');
}).one('change', function(){
}).on('change', function(){
firedEvents.push('change');

@@ -61,19 +56,19 @@ });

}).then(function () {
expect(firedEvents).to.eql([
'change',
'blur'
])
return retry(function(){
expect(firedEvents).to.eql([
'change',
'blur'
]);
});
});
});
it('click element should fire blur event on input', function(){
domTest('click element should fire blur event on input', function(browser, dom){
var blurred = false;
dom.insert('<input type="text" class="input"><button>button</button>');
dom.insert('<input type="text" class="input" />');
dom.insert('<button>button</button>');
dom.el.find('.input').on('blur', function(e){
if (e.target.className === 'input') {
blurred = true;
}
blurred = true;
})

@@ -84,22 +79,24 @@

}).then(function(){
expect(blurred).to.be.true
return retry(function(){
expect(blurred).to.be.true;
});
});
});
it('select element should fire blur event on input', function(){
domTest('select element should fire blur event on input', function(browser, dom, $){
var blurred = false;
dom.insert('<input type="text" class="input"><select><option>one</option></select>');
dom.insert('<select><option>one</option></select>');
dom.insert('<input type="text" class="input"></input>');
dom.el.find('input').on('blur', function(e){
blurred = true;
});
dom.el.find('.input').on('blur', function(e){
if (e.target.className === 'input') {
blurred = true;
}
})
return browser.find('.input').typeIn('first').then(function(){
return browser.find('select').select({text: 'one'});
}).then(function(){
expect(blurred).to.be.true
return retry(function(){
expect(blurred).to.be.true;
});
});

@@ -109,4 +106,4 @@ });

describe('callbacks on interaction', function () {
it('fires events on clicks', function () {
var button = dom.insert('<button>a button</button>')[0];
domTest('fires events on clicks', function (browser, dom) {
var button = dom.insert('<button>a button</button>');

@@ -120,8 +117,8 @@ var event;

expect(event.type).to.equal('click');
expect(event.element).to.equal(button);
expect(event.element[0]).to.equal(button[0]);
});
});
it('fires events on typeIn', function () {
var input = dom.insert('<input></input>')[0];
domTest('fires events on typeIn', function (browser, dom) {
var input = dom.insert('<input></input>');

@@ -136,8 +133,8 @@ var event;

expect(event.text).to.equal('some text');
expect(event.element).to.equal(input);
expect(event.element[0]).to.equal(input[0]);
});
});
it('fires events on typeIn', function () {
var editorDiv = dom.insert('<div class="editor"></div>')[0];
domTest('fires events on typeIn', function (browser, dom) {
var editorDiv = dom.insert('<div class="editor"></div>');

@@ -152,8 +149,8 @@ var event;

expect(event.html).to.equal('some <b>html</b>');
expect(event.element).to.equal(editorDiv);
expect(event.element[0]).to.equal(editorDiv[0]);
});
});
it('fires events on select', function () {
var select = dom.insert('<select><option>one</option></select>')[0];
domTest('fires events on select', function (browser, dom) {
var select = dom.insert('<select><option>one</option></select>');

@@ -164,8 +161,8 @@ var event;

event = e;
}).find('select').select('one').then(function () {
}).find('select').select({text: 'one'}).then(function () {
expect(event, 'expected event to fire').to.not.be.undefined;
expect(event.type).to.equal('select option');
expect(event.value).to.equal('one');
expect(event.optionElement).to.equal(select.firstChild);
expect(event.element).to.equal(select);
expect(event.optionElement[0]).to.equal(select.find('option')[0]);
expect(event.element[0]).to.equal(select[0]);
});

@@ -172,0 +169,0 @@ });

@@ -1,13 +0,5 @@

var browser = require('..');
var createTestDom = require('./createTestDom');
var $ = require('jquery');
var domTest = require('./domTest');
describe('find', function () {
var dom;
beforeEach(function(){
dom = createTestDom();
});
it('should eventually find an element', function () {
domTest('should eventually find an element', function (browser, dom) {
var promise = browser.find('.element').shouldExist();

@@ -20,5 +12,5 @@

it('should eventually find an element using a filter', function () {
domTest('should eventually find an element using a filter', function (browser, dom) {
var promise = browser.find('.element').filter(function (element) {
return element.classList.contains('correct');
return element.hasClass('correct');
}, 'has class "correct"').element();

@@ -30,7 +22,7 @@

return promise.then(function (element) {
expect(element.className).to.equal('element correct');
expect(element.attr('class')).to.equal('element correct');
});
});
it('should eventually find an element with the right text', function () {
domTest('should eventually find an element with the right text', function (browser, dom) {
var promise = browser.find('.element', {text: 'green'}).element();

@@ -42,9 +34,9 @@

return promise.then(function (element) {
expect(element.innerText).to.equal('green');
expect(element.text()).to.equal('green');
});
});
it('filter fails with the right message', function () {
domTest('filter fails with the right message', function (browser, dom) {
var promise = browser.find('.element').filter(function (element) {
return element.classList.contains('correct');
return element.hasClass('correct');
}, 'has class "correct"').element();

@@ -58,3 +50,3 @@

it('should eventually find an element in an iframe', function(){
domTest('should eventually find an element in an iframe', function(browser, dom){
var iframe = document.createElement('iframe');

@@ -72,6 +64,10 @@ iframe.src = '/base/test/page1.html';

});
});
}, {vdom: false});
it('can find things in an iframe', function(){
dom.eventuallyInsert('<iframe src="/base/test/page2.html"></iframe>');
domTest('can find things in an iframe', function(browser, dom){
var iframe = document.createElement('iframe');
iframe.src = '/base/test/page2.html';
iframe.width = 700;
iframe.height = 1000;
dom.el.append(iframe);

@@ -81,8 +77,8 @@ return browser.find('iframe').element().then(function(iframe){

});
});
}, {vdom: false});
it('calls a function for each element found', function(){
domTest('calls a function for each element found', function(browser, dom){
var promise = browser.find('span').elements();
dom.eventuallyInsert('<div><span>a</span><span>b</span></div>');
dom.insert('<div><span>a</span><span>b</span></div>');

@@ -94,10 +90,11 @@ return promise.then(function(elements){

describe('visibility', function(){
it('should not find an element that is visually hidden', function(){
domTest('should not find an element that is visually hidden', function(browser, dom){
dom.insert('<div class="element">hello <span style="display:none;">world</span></div>');
return browser.find('.element > span').shouldNotExist();
});
}, {vdom: false});
it('should find an element that is visually hidden when visibleOnly = false', function(){
domTest('should find an element that is visually hidden when visibleOnly = false', function(browser, dom){
dom.insert('<div class="element">hello <span style="display:none;">world</span></div>');

@@ -108,3 +105,3 @@

it('should find elements that are visually hidden because of how html renders them', function(){
domTest('should find elements that are visually hidden because of how html renders them', function(browser, dom){
dom.insert('<select><option>First</option><option>Second</option></select>');

@@ -114,4 +111,5 @@ return browser.find('select option').shouldHave({text: ['First', 'Second']});

});
describe('containing', function () {
it('eventually finds an element containing another element', function () {
domTest('eventually finds an element containing another element', function (browser, dom) {
var promise = browser.find('.outer').containing('.inner').shouldExist();

@@ -122,3 +120,3 @@

dom.insert('<div class="outer"><div class="inner">good</div></div>');
}, 200);
}, 10);

@@ -128,3 +126,3 @@ return promise;

it('element returns the outer element', function () {
domTest('element returns the outer element', function (browser, dom) {
var promise = browser.find('.outer').containing('.inner').element();

@@ -135,10 +133,10 @@

dom.insert('<div class="outer"><div class="inner">good</div></div>');
}, 200);
}, 10);
return promise.then(function (element) {
expect($(element).is('.outer')).to.be.true;
expect(element.hasClass('outer')).to.be.true;
});
});
it('errors with a usable css selector if it cant find something', function () {
domTest('errors with a usable css selector if it cant find something', function (browser, dom) {
var promise = browser.find('.outer').find('.not-there').element();

@@ -150,6 +148,6 @@

expect(promise).to.be.rejectedWith('expected to find: .outer .not-there')
return expect(promise).to.be.rejectedWith('expected to find: .outer .not-there')
});
it('errors with a usable css selector if it cant find an element containing another', function () {
domTest('errors with a usable css selector if it cant find an element containing another', function (browser, dom) {
var promise = browser.find('.outer').containing('.not-there').shouldExist();

@@ -161,6 +159,6 @@

expect(promise).to.be.rejectedWith('expected to find: .outer:has(.not-there)')
return expect(promise).to.be.rejectedWith('expected to find: .outer:has(.not-there)')
});
it("fails if it can't find an element containing another", function () {
domTest("fails if it can't find an element containing another", function (browser, dom) {
var promise = browser.find('.outer').containing('.inner').shouldExist();

@@ -172,2 +170,22 @@

return expect(promise).to.be.rejectedWith('expected to find: .outer:has(.inner)');
});
domTest('errors with a usable css selector if it cant find an element containing another', function (browser, dom) {
var promise = browser.find('.outer').containing('.not-there').shouldExist();
setTimeout(function () {
dom.insert('<div class="outer"><div>bad</div></div>');
}, 200);
return expect(promise).to.be.rejectedWith('expected to find: .outer:has(.not-there)')
});
domTest("fails if it can't find an element containing another", function (browser, dom) {
var promise = browser.find('.outer').containing('.inner').shouldExist();
setTimeout(function () {
dom.insert('<div class="outer"><div>bad</div></div>');
}, 10);
return expect(promise).to.be.rejected;

@@ -178,3 +196,3 @@ });

describe('chains', function () {
it('eventually finds the inner element, even if the outer element exists', function () {
domTest('eventually finds the inner element, even if the outer element exists', function (browser, dom) {
var promise = browser.find('.outer').find('.inner').shouldExist();

@@ -186,4 +204,4 @@

outer.append('<div class="inner">good</div>');
}, 200);
}, 200);
}, 10);
}, 10);

@@ -193,3 +211,3 @@ return promise;

it('fails to find the inner element if it never arrives', function () {
domTest('fails to find the inner element if it never arrives', function (browser, dom) {
var promise = browser.find('.outer').find('.inner').shouldExist();

@@ -199,3 +217,3 @@

var outer = dom.insert('<div class="outer"></div>');
}, 200);
}, 10);

@@ -202,0 +220,0 @@ return expect(promise).to.be.rejected;

@@ -8,1 +8,14 @@ require('lie/polyfill');

global.expect = chai.expect;
global.timeout = parseInt(typeof window == 'object' && window.__env__.BM_TIMEOUT || 100);
Promise.prototype.assertStackTrace = function(file) {
return this.then(function(){
throw new Error('This test should have thrown an error but did not. You need to fix this.');
}).catch(function(error){
var specLine = error.stack.split('\n').find(function(line){
return line.indexOf(file) != -1;
});
expect(specLine).to.include(file);
});
}

@@ -1,6 +0,6 @@

var browser = require('..');
var createTestDom = require('./createTestDom');
var create = require('../create');
describe('options', function () {
it('can set an option that is inerhited by components', function(){
var browser = create();
var parentComponent = browser.find('div').component({});

@@ -11,2 +11,14 @@ parentComponent.set({myOption: 'abc'});

});
it('can overide default timeout', function(){
var browser = create();
browser.set({timeout: 10});
var start = new Date();
return browser.find('.doesnt-exist').shouldExist().catch(function(){
var end = new Date();
var duration = end - start;
expect(duration).to.be.below(50);
});
});
});

@@ -1,13 +0,5 @@

var browser = require('..');
var createTestDom = require('./createTestDom');
var $ = require('jquery');
var domTest = require('./domTest');
describe('scope', function () {
var dom;
beforeEach(function(){
dom = createTestDom();
});
it('can scope with an element', function () {
domTest('can scope with an element', function (browser, dom, $) {
var red = dom.insert('<div><div class="element">red</div></div>');

@@ -25,3 +17,3 @@ var blue = dom.insert('<div><div class="element">blue</div></div>');

it('can scope with another finder', function () {
domTest('can scope with another finder', function (browser, dom, $) {
var red = dom.insert('<div class="red"><div class="element">red</div></div>');

@@ -28,0 +20,0 @@ var blue = dom.insert('<div class="blue"><div class="element">blue</div></div>');

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc