Skip to content

Commit

Permalink
feat(syntax): big syntax reboot, expose global $, $$, element, and by
Browse files Browse the repository at this point in the history
In an effort to make tests more readable and clear, a few more global variables
will now be exported.

`browser` is an instance of protractor. This was previously accessed using
`protractor.getInstance`.

`by` is a collection of element locators. Previously, this was `protractor.By`.

`$` is a shortcut for getting elements by css. `$('.foo')` === `element(by.css('.foo'))`

All changes should be backwards compatible, as tested with the new 'backwardscompat'
tests.
Closes #156.
  • Loading branch information
juliemr committed Oct 27, 2013
1 parent 3f0c249 commit a2cd6c8
Show file tree
Hide file tree
Showing 17 changed files with 830 additions and 36 deletions.
2 changes: 1 addition & 1 deletion lib/cli.js
Expand Up @@ -28,7 +28,7 @@ var argv = require('optimist').
alias('build', 'capabilities.build').
boolean('verbose').
boolean('includeStackTrace').
alias('verbose', 'jasmineNodeOpts.verbose').
alias('verbose', 'jasmineNodeOpts.isVerbose').
alias('includeStackTrace', 'jasmineNodeOpts.includeStackTrace').
check(function(arg) {
if (arg._.length > 1) {
Expand Down
20 changes: 20 additions & 0 deletions lib/locators.js
Expand Up @@ -76,7 +76,9 @@ ProtractorBy.prototype.selectedOption = function(model) {
}
};
};

/**
* @DEPRECATED - use 'model' instead.
* Usage:
* <input ng-model="user" type="text"/>
* ptor.findElement(protractor.By.input("user"));
Expand All @@ -94,6 +96,24 @@ ProtractorBy.prototype.input = function(model) {
};
};

/**
* Usage:
* <input ng-model="user" type="text"/>
* ptor.findElement(protractor.By.model("user"));
*/
ProtractorBy.prototype.model = function(model) {
return {
findOverride: function(driver, using) {
return driver.findElement(
webdriver.By.js(clientSideScripts.findInput), using, model);
},
findArrayOverride: function(driver, using) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findInputs), using, model);
}
};
};

/**
* Usage:
* <textarea ng-model="user"></textarea>
Expand Down
147 changes: 116 additions & 31 deletions lib/protractor.js
Expand Up @@ -7,6 +7,11 @@ var ProtractorBy = require('./locators.js').ProtractorBy;

var DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!';

var WEB_ELEMENT_FUNCTIONS = [
'click', 'sendKeys', 'getTagName', 'getCssValue', 'getAttribute', 'getText',
'getSize', 'getLocation', 'isEnabled', 'isSelected', 'submit', 'clear',
'isDisplayed', 'getOuterHtml', 'getInnerHtml'];

/**
* Mix in other webdriver functionality to be accessible via protractor.
*/
Expand All @@ -17,6 +22,8 @@ for (foo in webdriver) {
/**
* Mix a function from one object onto another. The function will still be
* called in the context of the original object.
*
* @private
* @param {Object} to
* @param {Object} from
* @param {string} fnName
Expand All @@ -28,7 +35,93 @@ var mixin = function(to, from, fnName, setupFn) {
setupFn();
}
return from[fnName].apply(from, arguments);
};
};

/**
* Build the helper 'element' function for a given instance of Protractor.
*
* @private
* @param {Protractor} ptor
* @return {function(webdriver.Locator): ElementFinder}
*/
var buildElementHelper = function(ptor) {
var element = function(locator) {
var elementFinder = {};
var webElementFns = WEB_ELEMENT_FUNCTIONS.concat(
['findElement', 'findElements', 'isElementPresent',
'evaluate', '$', '$$']);
webElementFns.forEach(function(fnName) {
elementFinder[fnName] = function() {
var elem = ptor.findElement(locator);
return elem[fnName].apply(elem, arguments);
};
});

elementFinder.find = function() {
return ptor.findElement(locator);
};

elementFinder.isPresent = function() {
return ptor.isElementPresent(locator);
};

return elementFinder;
};

/**
* @type {function(webdriver.Locator): ElementArrayFinder}
*/
element.all = function(locator) {
var elementArrayFinder = {};

elementArrayFinder.count = function() {
return ptor.findElements(locator).then(function(arr) {
return arr.length;
});
};

elementArrayFinder.get = function(index) {
var id = ptor.findElements(locator).then(function(arr) {
return arr[index];
});
return new webdriver.WebElement(ptor.driver, id);
};

elementArrayFinder.then = function() {
return ptor.findElements(locator);
};

return elementArrayFinder;
}

return element;
};

/**
* Build the helper '$' function for a given instance of Protractor.
*
* @private
* @param {Protractor} ptor
* @return {function(string): ElementFinder}
*/
var buildCssHelper = function(ptor) {
return function(cssSelector) {
return buildElementHelper(ptor)(webdriver.By.css(cssSelector));
};
};

/**
* Build the helper '$$' function for a given instance of Protractor.
*
* @private
* @param {Protractor} ptor
* @return {function(string): ElementArrayFinder}
*/
var buildMultiCssHelper = function(ptor) {
return function(cssSelector) {
return buildElementHelper(ptor).all(webdriver.By.css(cssSelector));
};
};

/**
Expand Down Expand Up @@ -63,6 +156,27 @@ var Protractor = function(webdriver, opt_baseUrl, opt_rootElement) {
*/
this.driver = webdriver;

/**
* Helper function for finding elements.
*
* @type {function(webdriver.Locator): ElementFinder}
*/
this.element = buildElementHelper(this);

/**
* Helper function for finding elements by css.
*
* @type {function(string): ElementFinder}
*/
this.$ = buildCssHelper(this);

/**
* Helper function for finding arrays of elements by css.
*
* @type {function(string): ElementArrayFinder}
*/
this.$$ = buildMultiCssHelper(this);

/**
* All get methods will be resolved against this base URL. Relative URLs are =
* resolved the way anchor tags resolve.
Expand Down Expand Up @@ -134,14 +248,10 @@ Protractor.prototype.waitForAngular = function() {
*/
Protractor.prototype.wrapWebElement = function(element) {
var thisPtor = this;
// Before any of these WebElement functions, Protractor will wait to make sure
// Before any of the WebElement functions, Protractor will wait to make sure
// Angular is synched up.
var functionsToSync = [
'click', 'sendKeys', 'getTagName', 'getCssValue', 'getAttribute', 'getText',
'getSize', 'getLocation', 'isEnabled', 'isSelected', 'submit', 'clear',
'isDisplayed', 'getOuterHtml', 'getInnerHtml'];
var originalFns = {};
functionsToSync.forEach(function(name) {
WEB_ELEMENT_FUNCTIONS.forEach(function(name) {
originalFns[name] = element[name];
element[name] = function() {
thisPtor.waitForAngular();
Expand Down Expand Up @@ -254,18 +364,6 @@ Protractor.prototype.wrapWebElement = function(element) {
return element;
};

/**
* Shortcut for querying the document directly with css.
*
* @param {string} selector a css selector
* @see webdriver.WebDriver.findElement
* @return {!webdriver.WebElement}
*/
Protractor.prototype.$ = function(selector) {
var locator = protractor.By.css(selector);
return this.findElement(locator);
};

/**
* Waits for Angular to finish rendering before searching for elements.
* @see webdriver.WebDriver.findElement
Expand All @@ -284,19 +382,6 @@ Protractor.prototype.findElement = function(locator, varArgs) {
return this.wrapWebElement(found);
};

/**
* Shortcut for querying the document directly with css.
*
* @param {string} selector a css selector
* @see webdriver.WebDriver.findElements
* @return {!webdriver.promise.Promise} A promise that will be resolved to an
* array of the located {@link webdriver.WebElement}s.
*/
Protractor.prototype.$$ = function(selector) {
var locator = protractor.By.css(selector);
return this.findElements(locator);
};

/**
* Waits for Angular to finish rendering before searching for elements.
* @see webdriver.WebDriver.findElements
Expand Down
11 changes: 8 additions & 3 deletions lib/runner.js
Expand Up @@ -191,16 +191,21 @@ var runJasmineTests = function() {

sessionId = session.getId();

var ptor = protractor.wrapDriver(
var browser = protractor.wrapDriver(
driver,
config.baseUrl,
config.rootElement)
ptor.params = config.params;
browser.params = config.params;

protractor.setInstance(ptor);
protractor.setInstance(browser);

// Export protractor to the global namespace to be used in tests.
global.protractor = protractor;
global.browser = browser;
global.$ = browser.$;
global.$$ = browser.$$;
global.element = browser.element;
global.by = protractor.By;

// Let the configuration configure the protractor instance before running
// the tests.
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
25 changes: 25 additions & 0 deletions spec/backwardscompatConf.js
@@ -0,0 +1,25 @@
// The main suite of Protractor tests.
exports.config = {
seleniumServerJar: './selenium/selenium-server-standalone-2.35.0.jar',
chromeDriver: './selenium/chromedriver',

seleniumAddress: 'http://localhost:4444/wd/hub',

// Spec patterns are relative to this directory.
specs: [
'backwardscompat/*_spec.js'
],

capabilities: {
'browserName': 'chrome'
},

baseUrl: 'http://localhost:8000',

params: {
login: {
user: 'Jane',
password: '1234'
}
}
};

0 comments on commit a2cd6c8

Please sign in to comment.