Socket
Socket
Sign inDemoInstall

protractor

Package Overview
Dependencies
Maintainers
2
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

protractor - npm Package Compare versions

Comparing version 1.6.1 to 1.7.0

docs/browser-support.md

129

bin/elementexplorer.js

@@ -35,122 +35,13 @@ #!/usr/bin/env node

*/
console.log('Please use "protractor [configFile] [options] --elementExplorer"' +
' for full functionality\n');
var webdriver = require('selenium-webdriver');
var protractor = require('../lib/protractor.js');
var repl = require('repl');
var util = require('util');
var vm = require('vm');
if (process.argv.length > 3) {
console.log('usage: elementexplorer.js [urL]');
process.exit(1);
} else if (process.argv.length === 3) {
process.argv[2] = ('--baseUrl=' + process.argv[2]);
}
var driver, browser;
var INITIAL_SUGGESTIONS = [
'element(by.id(\'\'))',
'element(by.css(\'\'))',
'element(by.name(\'\'))',
'element(by.binding(\'\'))',
'element(by.xpath(\'\'))',
'element(by.tagName(\'\'))',
'element(by.className(\'\'))'
];
var list = function(locator) {
return browser.findElements(locator).then(function(arr) {
var found = [];
for (var i = 0; i < arr.length; ++i) {
arr[i].getText().then(function(text) {
found.push(text);
});
}
return found;
});
};
var flowEval = function(code, context, file, callback) {
var vmErr,
result,
flow = webdriver.promise.controlFlow();
flow.execute(function() {
try {
result = vm.runInThisContext(code, file);
} catch (e) {
vmErr = e;
callback(vmErr, null);
}
if (vmErr && process.domain) {
process.domain.emit('error', vmErr);
process.domain.exit();
}
if (webdriver.promise.isPromise(result)) {
return result.then(function(val) {return val});
} else {
return result;
}
}).then(function(res) {
if (!vmErr) {
callback(null, res);
}
}, function(err) {
callback('There was a webdriver error: ' + err.name + ' ' + err.message,
null);
});
};
var startRepl = function() {
var flowRepl = repl.start({
'useGlobal': true,
'eval': flowEval
});
var originalComplete = flowRepl.complete;
flowRepl.complete = function(line, completeCallback) {
if (line == '') {
completeCallback(null, [INITIAL_SUGGESTIONS, '']);
} else {
originalComplete.apply(this, arguments);
}
};
flowRepl.on('exit', function() {
driver.quit();
util.puts('Shutting down. Goodbye.');
});
};
var startUp = function() {
driver = new webdriver.Builder().
usingServer('http://localhost:4444/wd/hub').
withCapabilities({'browserName': 'chrome'}).build();
driver.getSession().then(function(session) {
driver.manage().timeouts().setScriptTimeout(11000);
browser = protractor.wrapDriver(driver);
// Set up globals to be available from the command line.
global.driver = driver;
global.protractor = protractor;
global.browser = browser;
global.$ = browser.$;
global.$$ = browser.$$;
global.element = browser.element;
global.by = global.By = protractor.By;
global.list = list;
util.puts('Type <tab> to see a list of locator strategies.');
util.puts('Use the `list` helper function to find elements by strategy:');
util.puts(' e.g., list(by.binding(\'\')) gets all bindings.');
util.puts('');
var url = process.argv[2] || 'about:blank';
util.puts('Getting page at: ' + url);
driver.get(url);
startRepl();
});
};
startUp();
process.argv.push('--elementExplorer');
require('../lib/cli.js');
{
"webdriverVersions": {
"selenium": "2.44.0",
"chromedriver": "2.13",
"chromedriver": "2.14",
"iedriver": "2.44.0"
}
}

@@ -34,4 +34,9 @@ Contributing

Loosely, follow the [Angular contribution rules](http://docs.angularjs.org/misc/contribute).
Loosely, follow the [Angular contribution rules](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
* If your PR changes any behavior or fixes an issue, it should have an associated test.
* New features should be general and as simple as possible.
* Breaking changes should be avoided if possible.
* All pull requests require review. No PR will be submitted without a comment from a team member stating LGTM (Looks good to me).
Protractor specific rules

@@ -48,3 +53,3 @@ -------------------------

Protractor follows AngularJS's [commit message format](https://docs.google.com/a/google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.z8a3t6ehl060).
Please write meaningful commit messages - they are used to generate the changelog, so the commit message should tell a user everything they need to know about a commit. Protractor follows AngularJS's [commit message format](https://docs.google.com/a/google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.z8a3t6ehl060).

@@ -56,4 +61,2 @@ In summary, this style is

<body>
<BLANK LINE>
<footer>

@@ -60,0 +63,0 @@ Where `<type>` is one of [feat, fix, docs, style, refactor, test, chore] and

@@ -8,21 +8,8 @@ Setting Up the Browser

---------------
Protractor support for a particular browser is tied to the capabilities available in the driver for that browser. Notably, Protractor requires the driver to implement asynchronous script execution.
Protractor supports the two latest major versions of Chrome, Firefox, Safari, and IE.
| Driver | Support | Known Issues |
|------------------------|--------------|-----------------|
|ChromeDriver |Yes | |
|FirefoxDriver |Yes |[#480](https://github.com/angular/protractor/issues/480) clicking options doesn't update the model|
|SafariDriver |Yes |[#481](https://github.com/angular/protractor/issues/481) minus key doesn't work, SafariDriver does not support modals, [#1051](https://github.com/angular/protractor/issues/1051) We see occasional page loading timeouts|
|IEDriver |Yes |[#778](https://github.com/angular/protractor/issues/778), can be slow, [#1052](https://github.com/angular/protractor/issues/1052) often times out waiting for page load|
|OperaDriver |No | |
|ios-Driver |No | |
|Appium - iOS/Safari |Yes* | drag and drop not supported (session/:sessionid/buttondown unimplemented) |
|Appium - Android/Chrome |Yes* | |
|Selendroid |Yes* | |
Please see [Browser Support](/docs/browser-support.md) for a full list of
supported browsers and known issues.
* These drivers are not yet in the Protractor smoke tests.
Configuring Browsers

@@ -56,2 +43,3 @@ --------------------

Adding Chrome-Specific Options

@@ -71,2 +59,3 @@ ------------------------------

Testing Against Multiple Browsers

@@ -73,0 +62,0 @@ ---------------------------------

@@ -173,9 +173,7 @@ Debugging Protractor Tests

Currently, the explorer runs only with chrome and expects a standalone Selenium
Server to be running at http://localhost:4444 (see [Setting Up the Selenium Server](/docs/server-setup.md)).
To run element explorer, simply run protractor as you normally would, but pass in
the flag --elementExplorer:
From the Protractor directory, run with:
protractor --elementExplorer
node ./bin/elementexplorer.js <urL>
This will load up the URL on WebDriver and put the terminal into a REPL loop.

@@ -185,16 +183,32 @@ You will see a > prompt. The `browser`, `element` and `protractor` variables will

> element(by.id('foobar')).getText()
> browser.get('http://www.angularjs.org')
or
> browser.get('http://www.angularjs.org')
> element(by.id('foobar')).getText()
To get a list of functions you can call, try:
Typing tab at a blank prompt will fill in a suggestion for finding
elements. You can also use the `list(locator)` command to list all elements
matching a locator.
> browser
Element explorer will start chrome by default. However, you can specify
another browser, change browser settings, or specify any other config that you
normally would with your protractor test. To do this, pass configs to
protractor like you normally would,
but with the `--elementExplorer` flag set:
Typing tab at a blank prompt will fill in a suggestion for finding
elements.
protractor [configFile] [options] --elementExplorer
For example, to connect to ChromeDriver directly, use
protractor --directConnect --elementExplorer
Element explore will ignore your specs, not set up your framework (e.g. jasmine,
mocha, cucumber), and only allow you to pass in 1 capability, but will honor
every other parameter in your config.
Note `baseUrl` is used here as the initial page, i.e. element explorer will try
to navigate to `baseUrl` automatically on start.
Taking Screenshots

@@ -201,0 +215,0 @@ ------------------

@@ -44,3 +44,3 @@ FAQ

-----------------------------
The last two major versions of Chrome, Firefox, IE, and Safari. See details at [Setting Up the Browser](/docs/browser-setup.md).
The last two major versions of Chrome, Firefox, IE, and Safari. See details at [Setting Up the Browser](/docs/browser-setup.md) and [Browser Support](/docs/browser-support.md).

@@ -95,5 +95,6 @@ The result of `getText` from an input element is always empty

The method to take a screenshot automatically on failure would depend on the type of failure.
* For failures of entire specs (such as timeout or an expectation within the spec failed), you can add a reporter as such:
* For failures of entire specs (such as timeout or an expectation within the spec failed), you can add a reporter as below:
```javascript
// Note: this is using Jasmine 1.3 reporter syntax.
jasmine.getEnv().addReporter(new function() {

@@ -100,0 +101,0 @@ this.reportSpecResults = function(spec) {

@@ -46,3 +46,3 @@ Choosing a Framework

Options for Mocha such as 'reporter' and 'slow' can be given in the [config file](https://github.com/angular/protractor/blob/master/spec/mochaConf.js) with mochaOpts:
Options for Mocha such as 'reporter' and 'slow' can be given in the [config file](../spec/mochaConf.js) with mochaOpts:

@@ -56,3 +56,3 @@ ```js

For a full example, see Protractor’s own test: [/spec/mocha](https://github.com/angular/protractor/tree/master/spec/mocha).
For a full example, see Protractor’s own test: [/spec/mocha](../spec/mocha).

@@ -63,3 +63,3 @@

_Note: Limited support for Cucumber is available as of January 2014. Support for Cucumber in Protractor is maintained by the community, so bug fixes may be slow. For more information, see the [Cucumber GitHub site](https://github.com/cucumber/cucumber-js)._
_Note: Limited support for Cucumber is available as of January 2015. Support for Cucumber in Protractor is maintained by the community, so bug fixes may be slow. For more information, see the [Cucumber GitHub site](https://github.com/cucumber/cucumber-js)._

@@ -73,3 +73,3 @@

Set the 'framework' property to cucumber, either by adding `framework: 'cucumber'` to the [config file](https://github.com/angular/protractor/blob/master/spec/cucumberConf.js) or by adding `--framework=cucumber` to the command line.
Set the 'framework' property to cucumber, either by adding `framework: 'cucumber'` to the [config file](../spec/cucumberConf.js) or by adding `--framework=cucumber` to the command line.

@@ -84,4 +84,10 @@ Options for Cucumber such as 'format' can be given in the config file with cucumberOpts:

For a full example, see Protractor’s own test: [/spec/cucumber](https://github.com/angular/protractor/tree/master/spec/cucumber).
For a full example, see Protractor’s own test: [/spec/cucumber](../spec/cucumber).
Using a Custom Framework
------------------------
Check section [Framework Adapters for Protractor](../lib/frameworks/README.md) specifically [Custom Frameworks](../lib/frameworks/README.md#custom-frameworks)
Upgrading from Jasmine 1.3 to 2.x
=================================
First, please read [Jasmine's official upgrade documentation](http://jasmine.github.io/2.0/upgrading.html).
First, please read [Jasmine's official upgrade documentation](http://jasmine.github.io/2.1/upgrading.html).

@@ -108,1 +108,8 @@ ### In your conf file

```
#### Reporters
The syntax for custom reporters has changed for Jasmine2. If you were previously
adding reporters from a node module, such as the `jasmine-reporters` package on
npm, make sure you upgrade to a version which supports Jasmine2. If you are
writing your own reporter, see the [Jasmine docs on custom reporters](http://jasmine.github.io/2.1/custom_reporter.html).

@@ -105,3 +105,3 @@ Using Locators

// Get my index (starting at 0).
// Get by index (starting at 0).
element.all(locator).get(index);

@@ -108,0 +108,0 @@

@@ -80,3 +80,3 @@ // Reference Configuration File

specs: [
'spec/*_spec.js',
'spec/*_spec.js'
],

@@ -111,2 +111,7 @@

// Name of the process executing this capability. Not used directly by
// protractor or the browser, but instead pass directly to third parties
// like SauceLabs as the name of the job running this test
name: 'Unnamed Job',
// Number of times to run this set of capabilities (in parallel, unless

@@ -141,7 +146,7 @@ // limited by maxSessions). Default is 1.

// If you need to resolve multiCapabilities asynchronously (i.e. wait for
// If you need to resolve multiCapabilities asynchronously (i.e. wait for
// server/proxy, set firefox profile, etc), you can specify a function here
// which will return either `multiCapabilities` or a promise to
// `multiCapabilities`.
// If this returns a promise, it is resolved immediately after
// If this returns a promise, it is resolved immediately after
// `beforeLaunch` is run, and before any driver is set up.

@@ -182,3 +187,3 @@ // If this is specified, both capabilities and multiCapabilities will be

beforeLaunch: function() {
// At this point, global variable 'protractor' object will NOT be set up,
// At this point, global variable 'protractor' object will NOT be set up,
// and globals from the test framework will NOT be available. The main

@@ -200,2 +205,10 @@ // purpose of this function should be to bring up test dependencies.

// 'outputdir/', true, true));
//
// If you need access back to the current configuration object,
// use a pattern like the following:
// browser.getProcessedConfig().then(function(config) {
// // config.capabilities is the CURRENT capability being run, if
// // you are using multiCapabilities.
// console.log('Executing capability', config.capabilities);
// });
},

@@ -236,3 +249,3 @@

// If true, protractor will restart the browser between each test.
// If true, protractor will restart the browser between each test.
// CAUTION: This will cause your tests to slow down drastically.

@@ -245,4 +258,12 @@ restartBrowserBetweenTests: false,

// Test framework to use. This may be jasmine, jasmine2, cucumber, or mocha.
// Test framework to use. This may be one of:
// jasmine, jasmine2, cucumber, mocha or custom.
//
// When the framework is set to "custom" you'll need to additionally
// set frameworkPath with the path relative to the config file or absolute
// framework: 'custom',
// frameworkPath: './frameworks/my_custom_jasmine.js',
// See github.com/angular/protractor/blob/master/lib/frameworks/README.md
// to comply with the interface details of your custom implementation.
//
// Jasmine is fully supported as a test and assertion framework.

@@ -269,3 +290,4 @@ // Mocha and Cucumber have limited beta support. You will need to include your

//
// See the full list at https://github.com/jasmine/jasmine-npm
// See https://github.com/jasmine/jasmine-npm/blob/master/lib/jasmine.js
// for the exact options available.
jasmineNodeOpts: {

@@ -272,0 +294,0 @@ // If true, print colors to the terminal.

@@ -72,3 +72,5 @@ Setting Up the Selenium Server

You can optionally set the [`name` property](referenceConf.js#L113) in a capability in order to give the jobs a name on the server. Otherwise they will just be called `Unnamed Job`.
Connecting Directly to Browser Drivers

@@ -75,0 +77,0 @@ --------------------------------------

@@ -22,2 +22,3 @@ Table of Contents

- [Protractor API](/docs/api.md)
- [Browser Support](/docs/browser-support.md)
- [Timeouts](/docs/timeouts.md)

@@ -24,0 +25,0 @@ - [The WebDriver Control Flow](/docs/control-flow.md)

@@ -54,2 +54,3 @@ /**

describe('troubleshoot', 'Turn on troubleshooting output').
describe('elementExplorer', 'Interactively test Protractor commands').
alias('browser', 'capabilities.browserName').

@@ -74,2 +75,7 @@ alias('name', 'capabilities.name').

if (argv.help) {
optimist.showHelp();
process.exit(0);
}
if (argv.version) {

@@ -128,3 +134,6 @@ console.log('Version ' + require(path.join(__dirname, '../package.json')).version);

}
if (!configFile && args.length < 3) {
if (!configFile && !argv.elementExplorer && args.length < 3) {
console.log('**you must either specify a configuration file ' +
'or at least 3 options. See below for the options:\n');
optimist.showHelp();

@@ -131,0 +140,0 @@ process.exit(1);

@@ -72,3 +72,3 @@ /**

var dataBinding = angular.element(bindings[i]).data('$binding');
if(dataBinding) {
if (dataBinding) {
var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;

@@ -85,3 +85,3 @@ if (exactMatch) {

}
}

@@ -98,2 +98,3 @@ }

* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {number} index The row index.

@@ -105,3 +106,12 @@ * @param {Element} using The scope of the search.

*/
functions.findRepeaterRows = function(repeater, index, using) {
functions.findRepeaterRows = function(repeater, exact, index, using) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
using = using || document;

@@ -116,3 +126,3 @@

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);

@@ -130,7 +140,7 @@ }

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
var row = [];
while (elem.nodeType != 8 ||
elem.nodeValue.indexOf(repeater) == -1) {
!repeaterMatch(elem.nodeValue, repeater, exact)) {
if (elem.nodeType == 1) {

@@ -146,3 +156,3 @@ row.push(elem);

var row = rows[index] || [], multiRow = multiRows[index] || [];
return [].concat(row, multiRow);
return [].concat(row, multiRow);
};

@@ -154,2 +164,3 @@

* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {Element} using The scope of the search.

@@ -159,3 +170,12 @@ *

*/
functions.findAllRepeaterRows = function(repeater, using) {
functions.findAllRepeaterRows = function(repeater, exact, using) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
using = using || document;

@@ -170,3 +190,3 @@

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);

@@ -181,6 +201,6 @@ }

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
while (elem.nodeType != 8 ||
elem.nodeValue.indexOf(repeater) == -1) {
!repeaterMatch(elem.nodeValue, repeater, exact)) {
if (elem.nodeType == 1) {

@@ -201,2 +221,3 @@ rows.push(elem);

* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {number} index The row index.

@@ -209,3 +230,12 @@ * @param {string} binding The column binding, e.g. '{{cat.name}}'.

*/
functions.findRepeaterElement = function(repeater, index, binding, using, rootSelector) {
functions.findRepeaterElement = function(repeater, exact, index, binding, using, rootSelector) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
var matches = [];

@@ -222,3 +252,3 @@ var root = document.querySelector(rootSelector || 'body');

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);

@@ -236,7 +266,7 @@ }

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
var row = [];
while (elem.nodeType != 8 ||
(elem.nodeValue && elem.nodeValue.indexOf(repeater) == -1)) {
while (elem.nodeType != 8 || (elem.nodeValue &&
!repeaterMatch(elem.nodeValue, repeater, exact))) {
if (elem.nodeType == 1) {

@@ -289,3 +319,3 @@ row.push(elem);

var dataBinding = angular.element(bindings[i]).data('$binding');
if(dataBinding) {
if (dataBinding) {
var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;

@@ -304,2 +334,3 @@ if (bindingName.indexOf(binding) != -1) {

* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {string} binding The column binding, e.g. '{{cat.name}}'.

@@ -311,3 +342,12 @@ * @param {Element} using The scope of the search.

*/
functions.findRepeaterColumn = function(repeater, binding, using, rootSelector) {
functions.findRepeaterColumn = function(repeater, exact, binding, using, rootSelector) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
var matches = [];

@@ -324,3 +364,3 @@ var root = document.querySelector(rootSelector || 'body');

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);

@@ -338,7 +378,7 @@ }

for (var i = 0; i < repeatElems.length; ++i) {
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
var row = [];
while (elem.nodeType != 8 ||
(elem.nodeValue && elem.nodeValue.indexOf(repeater) == -1)) {
while (elem.nodeType != 8 || (elem.nodeValue &&
!repeaterMatch(elem.nodeValue, repeater, exact))) {
if (elem.nodeType == 1) {

@@ -429,3 +469,3 @@ row.push(elem);

*
* @param {string} optionsDescriptor The descriptor for the option
* @param {string} optionsDescriptor The descriptor for the option
* (i.e. fruit for fruit in fruits).

@@ -635,3 +675,3 @@ * @param {Element} using The scope of the search.

* and the exception message is just "unknown error." These types of
* exceptins are the common case for dart2js code. This wrapping gives
* exceptions are the common case for dart2js code. This wrapping gives
* us the Dart stack trace and exception message.

@@ -638,0 +678,0 @@ */

@@ -23,3 +23,3 @@ var path = require('path'),

// Default configuration.
this.config_= {
this.config_ = {
specs: [],

@@ -150,3 +150,4 @@ multiCapabilities: [],

// This will not affect absolute paths.
['seleniumServerJar', 'chromeDriver', 'onPrepare', 'firefoxPath'].
['seleniumServerJar', 'chromeDriver', 'onPrepare', 'firefoxPath',
'frameworkPath'].
forEach(function(name) {

@@ -153,0 +154,0 @@ if (additionalConfig[name] &&

@@ -51,3 +51,3 @@ /*

DirectDriverProvider.prototype.getNewDriver = function() {
var driver;
var driver;
switch (this.config_.capabilities.browserName) {

@@ -70,3 +70,3 @@ case 'chrome':

driver = chrome.createDriver(
new webdriver.Capabilities(this.config_.capabilities), service);
new webdriver.Capabilities(this.config_.capabilities), service);
break;

@@ -73,0 +73,0 @@ case 'firefox':

@@ -24,3 +24,3 @@ /*

HostedDriverProvider.prototype.setupEnv = function() {
log.puts('Using the selenium server at ' +
log.puts('Using the selenium server at ' +
this.config_.seleniumAddress);

@@ -27,0 +27,0 @@ return q.fcall(function() {});

@@ -28,3 +28,3 @@ /*

SauceDriverProvider.prototype.updateJob = function(update) {
var self = this;

@@ -69,3 +69,3 @@ var deferredArray = this.drivers_.map(function(driver) {

'ondemand.saucelabs.com:80/wd/hub');
// Append filename to capabilities.name so that it's easier to identify tests.

@@ -72,0 +72,0 @@ if (this.config_.capabilities.name &&

@@ -54,3 +54,3 @@ var util = require('util');

* @constructor
* @param {Protractor} ptor A protractor instance.
* @param {Protractor} ptor A protractor instance.
* @param {function(): Array.<webdriver.WebElement>} getWebElements A function

@@ -66,3 +66,3 @@ * that returns a list of the underlying Web Elements.

var ElementArrayFinder = function(ptor, getWebElements, locator, opt_actionResults) {
this.ptor_ = ptor;
this.ptor_ = ptor;
this.getWebElements = getWebElements || null;

@@ -97,3 +97,3 @@ this.actionResults_ = opt_actionResults;

return new ElementArrayFinder(
this.ptor_, this.getWebElements, this.locator_, this.actionResults_);
this.ptor_, this.getWebElements, this.locator_, this.actionResults_);
};

@@ -149,3 +149,3 @@

var childrenPromiseList = [];
// For each parent web element, find their children and construct a
// For each parent web element, find their children and construct a
// list of Promise<List<child_web_element>>

@@ -201,3 +201,3 @@ parentWebElements.forEach(function(parentWebElement) {

*
* @param {function(ElementFinder, number): webdriver.WebElement.Promise} filterFn
* @param {function(ElementFinder, number): webdriver.WebElement.Promise} filterFn
* Filter function that will test if an element should be returned.

@@ -214,3 +214,3 @@ * filterFn can either return a boolean or a promise that resolves to a boolean.

parentWebElements.forEach(function(parentWebElement, index) {
var elementFinder =
var elementFinder =
ElementFinder.fromWebElement_(self.ptor_, parentWebElement, self.locator_);

@@ -255,3 +255,3 @@

* @return {ElementFinder} finder representing element at the given index.
*/
*/
ElementArrayFinder.prototype.get = function(index) {

@@ -261,3 +261,3 @@ var self = this;

return self.getWebElements().then(function(parentWebElements) {
var i = index;
var i = index;
if (i < 0) {

@@ -415,7 +415,7 @@ // wrap negative indices

*
* @return {Array.<ElementFinder>} Return a promise, which resolves to a list
* @return {Array.<ElementFinder>} Return a promise, which resolves to a list
* of ElementFinders specified by the locator.
*/
ElementArrayFinder.prototype.asElementFinders_ = function() {
var self = this;
var self = this;
return this.getWebElements().then(function(arr) {

@@ -426,3 +426,3 @@ var list = [];

});
return list;
return list;
});

@@ -537,7 +537,7 @@ };

/**
* Apply a reduce function against an accumulator and every element found
* Apply a reduce function against an accumulator and every element found
* using the locator (from left-to-right). The reduce function has to reduce
* every element into a single value (the accumulator). Returns promise of
* the accumulator. The reduce function receives the accumulator, current
* ElementFinder, the index, and the entire array of ElementFinders,
* every element into a single value (the accumulator). Returns promise of
* the accumulator. The reduce function receives the accumulator, current
* ElementFinder, the index, and the entire array of ElementFinders,
* respectively.

@@ -562,7 +562,7 @@ *

*
* @param {function(number, ElementFinder, number, Array.<ElementFinder>)}
* @param {function(number, ElementFinder, number, Array.<ElementFinder>)}
* reduceFn Reduce function that reduces every element into a single value.
* @param {*} initialValue Initial value of the accumulator.
* @param {*} initialValue Initial value of the accumulator.
* @return {!webdriver.promise.Promise} A promise that resolves to the final
* value of the accumulator.
* value of the accumulator.
*/

@@ -632,6 +632,6 @@ ElementArrayFinder.prototype.reduce = function(reduceFn, initialValue) {

*
* The ElementFinder can be treated as a WebElement for most purposes, in
* The ElementFinder can be treated as a WebElement for most purposes, in
* particular, you may perform actions (i.e. click, getText) on them as you
* would a WebElement. ElementFinders extend Promise, and once an action
* is performed on an ElementFinder, the latest result from the chain can be
* would a WebElement. ElementFinders extend Promise, and once an action
* is performed on an ElementFinder, the latest result from the chain can be
* accessed using then. Unlike a WebElement, an ElementFinder will wait for

@@ -641,5 +641,5 @@ * angular to settle before performing finds or actions.

* ElementFinder can be used to build a chain of locators that is used to find
* an element. An ElementFinder does not actually attempt to find the element
* until an action is called, which means they can be set up in helper files
* before the page is available.
* an element. An ElementFinder does not actually attempt to find the element
* until an action is called, which means they can be set up in helper files
* before the page is available.
*

@@ -679,3 +679,3 @@ * @alias element(locator)

// This filter verifies that there is only 1 element returned by the
// This filter verifies that there is only 1 element returned by the
// elementArrayFinder. It will warn if there are more than 1 element and

@@ -736,4 +736,4 @@ // throw an error if there are no elements.

// A shallow copy is all we need since the underlying fields can never be
// modified
return new ElementFinder(this.ptor_, this.parentElementArrayFinder);
// modified
return new ElementFinder(this.ptor_, this.parentElementArrayFinder);
};

@@ -778,3 +778,3 @@

*
* @return {webdriver.promise.Promise} Promise which contains the results of
* @return {webdriver.promise.Promise} Promise which contains the results of
* evaluating fn.

@@ -791,4 +791,5 @@ */

/**
* Calls to element may be chained to find an array of elements within a parent.
/**
* Calls to {@code all} may be chained to find an array of elements within a
* parent.
*

@@ -816,3 +817,3 @@ * @alias element(locator).all(locator)

/**
* Calls to element may be chained to find elements within a parent.
* Calls to {@code element} may be chained to find elements within a parent.
*

@@ -848,24 +849,20 @@ * @alias element(locator).element(locator)

/**
* Shortcut for querying the document directly with css.
* Calls to {@code $$} may be chained to find an array of elements within a
* parent.
*
* @alias $$(cssSelector)
* @alias element(locator).all(selector)
* @view
* <div class="count">
* <span class="one">First</span>
* <span class="two">Second</span>
* <div class="parent">
* <ul>
* <li class="one">First</li>
* <li class="two">Second</li>
* <li class="three">Third</li>
* </ul>
* </div>
*
* @example
* // The following protractor expressions are equivalent.
* var list = element.all(by.css('.count span'));
* expect(list.count()).toBe(2);
* var items = element(by.css('.parent')).$$('li')
*
* list = $$('.count span');
* expect(list.count()).toBe(2);
* expect(list.get(0).getText()).toBe('First');
* expect(list.get(1).getText()).toBe('Second');
*
* @param {string} selector a css selector
* @return {ElementArrayFinder} which identifies the
* array of the located {@link webdriver.WebElement}s.
* @return {ElementArrayFinder}
*/

@@ -877,18 +874,27 @@ ElementFinder.prototype.$$ = function(selector) {

/**
* Shortcut for querying the document directly with css.
* Calls to {@code $} may be chained to find elements within a parent.
*
* @alias $(cssSelector)
* @alias element(locator).$(selector)
* @view
* <div class="count">
* <span class="one">First</span>
* <span class="two">Second</span>
* <div class="parent">
* <div class="child">
* Child text
* <div>{{person.phone}}</div>
* </div>
* </div>
*
* @example
* var item = $('.count .two');
* expect(item.getText()).toBe('Second');
* // Chain 2 element calls.
* var child = element(by.css('.parent')).
* $('.child');
* expect(child.getText()).toBe('Child text\n555-123-4567');
*
* // Chain 3 element calls.
* var triple = element(by.css('.parent')).
* $('.child').
* element(by.binding('person.phone'));
* expect(triple.getText()).toBe('555-123-4567');
*
* @param {string} selector A css selector
* @return {ElementFinder} which identifies the located
* {@link webdriver.WebElement}
* @return {ElementFinder}
*/

@@ -924,3 +930,3 @@ ElementFinder.prototype.$ = function(selector) {

* for Angular to settle before making the check.
*
*
* @see ElementFinder.isPresent

@@ -968,1 +974,57 @@ *

};
/**
* Shortcut for querying the document directly with css.
*
* @alias $(cssSelector)
* @view
* <div class="count">
* <span class="one">First</span>
* <span class="two">Second</span>
* </div>
*
* @example
* var item = $('.count .two');
* expect(item.getText()).toBe('Second');
*
* @param {string} selector A css selector
* @return {ElementFinder} which identifies the located
* {@link webdriver.WebElement}
*/
var build$ = function(element, by) {
return function(selector) {
return element(by.css(selector));
};
};
exports.build$ = build$;
/**
* Shortcut for querying the document directly with css.
*
* @alias $$(cssSelector)
* @view
* <div class="count">
* <span class="one">First</span>
* <span class="two">Second</span>
* </div>
*
* @example
* // The following protractor expressions are equivalent.
* var list = element.all(by.css('.count span'));
* expect(list.count()).toBe(2);
*
* list = $$('.count span');
* expect(list.count()).toBe(2);
* expect(list.get(0).getText()).toBe('First');
* expect(list.get(1).getText()).toBe('Second');
*
* @param {string} selector a css selector
* @return {ElementArrayFinder} which identifies the
* array of the located {@link webdriver.WebElement}s.
*/
var build$$ = function(element, by) {
return function(selector) {
return element.all(by.css(selector));
};
};
exports.build$$ = build$$;

@@ -69,3 +69,3 @@ var ConfigParser = require('../configParser'),

var failedCount = 0;
var failedCount = 0;
// Add a listener into cucumber so that protractor can find out which

@@ -93,3 +93,3 @@ // steps passed/failed

if (originalHandleAfterScenarioEvent
if (originalHandleAfterScenarioEvent
&& typeof(originalHandleAfterScenarioEvent) === 'function') {

@@ -111,3 +111,3 @@ originalHandleAfterScenarioEvent(event, callback);

} else if (stepResult.isFailed()) {
scenarioFailed = true;
scenarioFailed = true;
var failureMessage = stepResult.getFailureException();

@@ -122,3 +122,3 @@ stepResults.assertions.push({

if (originalHandleStepResultEvent
if (originalHandleStepResultEvent
&& typeof(originalHandleStepResultEvent) === 'function') {

@@ -135,3 +135,3 @@ originalHandleStepResultEvent(event, callback);

var cucumberConf = Cucumber.Cli.Configuration(execOptions);
var runtime = Cucumber.Runtime(cucumberConf);
var runtime = Cucumber.Runtime(cucumberConf);
var formatter = cucumberConf.getFormatter();

@@ -138,0 +138,0 @@ addResultListener(formatter);

@@ -14,3 +14,3 @@ var util = require('util'),

exports.run = function(runner, specs) {
return q.promise(function (resolve) {
return q.promise(function(resolve) {
log.puts('Resolved spec files: ' + util.inspect(specs));

@@ -17,0 +17,0 @@ resolve({

@@ -48,3 +48,3 @@ var q = require('q');

errorMsg: item.passed() ? undefined : item.message,
stackTrace: item.passed() ? undefined : item.trace.stack
stackTrace: item.passed() ? undefined : item.trace.stack
});

@@ -64,3 +64,3 @@ });

return runner.runTestPreparer().then(function() {
return q.promise(function (resolve, reject) {
return q.promise(function(resolve, reject) {
var jasmineNodeOpts = runner.getConfig().jasmineNodeOpts;

@@ -78,3 +78,3 @@ var originalOnComplete = runner.getConfig().onComplete;

});
} catch(err) {
} catch (err) {
reject(err);

@@ -81,0 +81,0 @@ }

@@ -32,3 +32,3 @@ var q = require('q');

};
result.failedExpectations.forEach(function(item) {

@@ -65,3 +65,3 @@ entry.assertions.push({

// get to complete first.
var reporter = new RunnerReporter(runner);
var reporter = new RunnerReporter(runner);
jasmine.getEnv().addReporter(reporter);

@@ -71,4 +71,4 @@

jasmine.getEnv().specFilter = function(spec) {
var grepMatch = !jasmineNodeOpts ||
!jasmineNodeOpts.grep ||
var grepMatch = !jasmineNodeOpts ||
!jasmineNodeOpts.grep ||
spec.getFullName().match(new RegExp(jasmineNodeOpts.grep)) != null;

@@ -83,3 +83,3 @@ var invertGrep = !!(jasmineNodeOpts && jasmineNodeOpts.invertGrep);

return runner.runTestPreparer().then(function() {
return q.promise(function (resolve, reject) {
return q.promise(function(resolve, reject) {
if (jasmineNodeOpts && jasmineNodeOpts.defaultTimeoutInterval) {

@@ -99,3 +99,3 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = jasmineNodeOpts.defaultTimeoutInterval;

});
} catch(err) {
} catch (err) {
reject(err);

@@ -102,0 +102,0 @@ }

@@ -38,3 +38,3 @@ var q = require('q');

global.it.skip = global.xit = mochaAdapters.xit;
} catch(err) {
} catch (err) {
deferred.reject(err);

@@ -63,3 +63,3 @@ }

});
} catch(err) {
} catch (err) {
deferred.reject(err);

@@ -87,3 +87,3 @@ }

errorMsg: test.err.message,
stackTrace: test.err.stack
stackTrace: test.err.stack
}],

@@ -93,3 +93,3 @@ duration: test.duration

});
}).catch(function(reason) {
}).catch (function(reason) {
deferred.reject(reason);

@@ -96,0 +96,0 @@ });

@@ -39,1 +39,22 @@ Framework Adapters for Protractor

```
Custom Frameworks
-----------------
If you have created/adapted a custom framework and want it added to
Protractor core please send a PR so it can evaluated for addition as an
official supported framework. In the meantime you can instruct Protractor
to use your own framework via the config file:
```js
exports.config = {
// set to "custom" instead of jasmine/jasmine2/mocha/cucumber.
framework: 'custom',
// path relative to the current config file
frameworkPath: './frameworks/my_custom_jasmine.js',
};
```
More on this at [referenceConf](../../docs/referenceConf.js) "The test framework" section.
**Disclaimer**: current framework interface can change without a major version bump.

@@ -29,4 +29,4 @@ /**

* Keeps track of a list of task results. Provides method to add a new
* result, aggregate the results into a summary, count failures,
* and save results into a JSON file.
* result, aggregate the results into a summary, count failures,
* and save results into a JSON file.
*/

@@ -45,7 +45,7 @@ var taskResults_ = {

});
return specFailures;
return specFailures;
},
totalProcessFailures: function() {
var processFailures = 0;
var processFailures = 0;
this.results_.forEach(function(result) {

@@ -67,3 +67,3 @@ if (!result.failedCount && result.exitCode !== 0) {

var fs = require('fs');
fs.writeFileSync(filepath, json);
fs.writeFileSync(filepath, json);
},

@@ -73,3 +73,3 @@

var specFailures = this.totalSpecFailures();
var processFailures = this.totalProcessFailures();
var processFailures = this.totalProcessFailures();
this.results_.forEach(function(result) {

@@ -90,3 +90,3 @@ var capabilities = result.capabilities;

if (specFailures && processFailures) {
log_('overall: ' + specFailures + ' failed spec(s) and ' +
log_('overall: ' + specFailures + ' failed spec(s) and ' +
processFailures + ' process(es) failed to complete');

@@ -125,7 +125,6 @@ } else if (specFailures) {

helper.runFilenameOrFn_(config.configDir, config.beforeLaunch).then(function() {
// Set `multicapabilities` using `capabilities`, `multicapabilites`,
// `getMultiCapabilities()`, or default
return q.promise(function(resolve) {
if (config.getMultiCapabilities &&
// 1) If getMultiCapabilities is set, resolve that as `multiCapabilities`.
if (config.getMultiCapabilities &&
typeof config.getMultiCapabilities === 'function') {

@@ -145,2 +144,4 @@ if (config.multiCapabilities.length || config.capabilities) {

}).then(function() {
// 2) Set `multicapabilities` using `capabilities`, `multicapabilites`,
// or default
if (config.capabilities) {

@@ -163,2 +164,22 @@ if (config.multiCapabilities.length) {

}).then(function() {
// 3) If we're in `elementExplorer` mode, run only that.
if (config.elementExplorer || config.framework === 'explorer') {
if (config.multiCapabilities.length != 1) {
throw new Error('Must specify only 1 browser while using elementExplorer');
} else {
config.capabilities = config.multiCapabilities[0];
}
config.framework = 'explorer';
var Runner = require('./runner');
var runner = new Runner(config);
return runner.run().then(function(exitCode) {
process.exit(exitCode);
}, function(err) {
log_(err);
process.exit(1);
});
}
}).then(function() {
// 4) Run tests.
var scheduler = new TaskScheduler(config);

@@ -170,3 +191,3 @@

} else if (scheduler.numTasksOutstanding() > 0) {
log_('BUG: launcher exited with ' +
log_('BUG: launcher exited with ' +
scheduler.numTasksOutstanding() + ' tasks remaining');

@@ -193,8 +214,8 @@ process.exit(RUNNERS_FAILED_EXIT_CODE);

var totalTasks = scheduler.numTasksOutstanding();
var totalTasks = scheduler.numTasksOutstanding();
var forkProcess = false;
if (totalTasks > 1) { // Start new processes only if there are >1 tasks.
forkProcess = true;
forkProcess = true;
if (config.debug) {
throw new Error('Cannot run in debug mode with ' +
throw new Error('Cannot run in debug mode with ' +
'multiCapabilities, count > 1, or sharding');

@@ -220,5 +241,5 @@ }

}
log_(scheduler.countActiveTasks() +
log_(scheduler.countActiveTasks() +
' instance(s) of WebDriver still running');
}).catch(function(err) {
}).catch (function(err) {
log_('Error:', err.stack || err.message || err);

@@ -231,3 +252,3 @@ cleanUpAndExit(RUNNERS_FAILED_EXIT_CODE);

// the beginning. As a worker finishes a task, it will pick up the next task
// from the scheduler's queue until all tasks are gone.
// from the scheduler's queue until all tasks are gone.
for (var i = 0; i < scheduler.maxConcurrentTasks(); ++i) {

@@ -238,3 +259,3 @@ createNextTaskRunner();

// By now all runners have completed.
// By now all runners have completed.
deferred.promise.then(function() {

@@ -241,0 +262,0 @@ // Save results if desired

@@ -137,3 +137,3 @@ var util = require('util');

* expect(element(by.exactBinding('phone')).isPresent()).toBe(false);
*
*
* @param {string} bindingDescriptor

@@ -232,2 +232,70 @@ * @return {{findElementsOverride: findElementsOverride, toString: Function|string}}

// Generate either by.repeater or by.exactRepeater
function byRepeaterInner(exact) {
var name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater';
return function(repeatDescriptor) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findAllRepeaterRows,
repeatDescriptor, exact, using, rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterRows,
repeatDescriptor, exact, index, using, rootSelector));
},
toString: function toString() {
return name + '(' + repeatDescriptor + '").row("' + index + '")"';
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, exact, index, binding, using,
rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '").row("' + index +
'").column("' + binding + '")';
}
};
}
};
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterColumn,
repeatDescriptor, exact, binding, using, rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '").column("' +
binding + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, exact, index, binding, using, rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '").column("' +
binding + '").row("' + index + '")';
}
};
}
};
}
};
};
}

@@ -284,68 +352,27 @@ /**

* var divs = element.all(by.repeater('book in library'));
*
* @param {string} repeatDescriptor
* @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
*/
ProtractorBy.prototype.repeater = function(repeatDescriptor) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findAllRepeaterRows,
repeatDescriptor, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterRows,
repeatDescriptor, index, using, rootSelector));
},
toString: function toString() {
return 'by.repeater(' + repeatDescriptor + '").row("' + index + '")"';
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, index, binding, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '").row("' + index +
'").column("' + binding + '")';
}
};
}
};
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterColumn,
repeatDescriptor, binding, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '").column("' +
binding + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, index, binding, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '").column("' +
binding + '").row("' + index + '")';
}
};
}
};
}
};
};
ProtractorBy.prototype.repeater = byRepeaterInner(false);
/**
* Find an element by exact repeater.
*
* @view
* <li ng-repeat="person in peopleWithRedHair"></li>
* <li ng-repeat="car in cars | orderBy:year"></li>
*
* @example
* expect(element(by.exactRepeater('person in peopleWithRedHair')).isPresent()).toBe(true);
* expect(element(by.exactRepeater('person in people')).isPresent()).toBe(false);
* expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true);
*
* @param {string} repeatDescriptor
* @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
*/
ProtractorBy.prototype.exactRepeater = byRepeaterInner(true);
/**
* Find elements by CSS which contain a certain string.

@@ -360,3 +387,3 @@ *

* @example
* // Returns the DIV for the dog, but not cat.
* // Returns the li for the dog, but not cat.
* var dog = element(by.cssContainingText('.pet', 'Dog'));

@@ -408,2 +435,26 @@ */

/**
* Find an element by css selector within the Shadow DOM.
*
* @alias by.deepCss(selector)
* @view
* <div>
* <span id="outerspan">
* <"shadow tree">
* <span id="span1"></span>
* <"shadow tree">
* <span id="span2"></span>
* </>
* </>
* </div>
* @example
* spans = element.all(by.deepCss('span'));
* expect(spans.count()).toEqual(3);
*/
ProtractorBy.prototype.deepCss = function(selector) {
// TODO(julie): syntax will change from /deep/ to >>> at some point.
// When that is supported, switch it here.
return webdriver.By.css('* /deep/ ' + selector);
};
exports.ProtractorBy = ProtractorBy;

@@ -7,5 +7,8 @@ var url = require('url');

var ElementFinder = require('./element').ElementFinder;
var build$ = require('./element').build$;
var build$$ = require('./element').build$$;
var clientSideScripts = require('./clientsidescripts.js');
var ProtractorBy = require('./locators.js').ProtractorBy;
var ExpectedConditions = require('./expectedConditions.js');

@@ -41,2 +44,7 @@ /* global angular */

/**
* @type {ExpectedConditions}
*/
exports.ExpectedConditions = new ExpectedConditions();
/**
* Mix a function from one object onto another. The function will still be

@@ -96,3 +104,3 @@ * called in the context of the original object.

for (var method in webdriverInstance) {
if(!this[method] && typeof webdriverInstance[method] == 'function') {
if (!this[method] && typeof webdriverInstance[method] == 'function') {
if (methodsToSync.indexOf(method) !== -1) {

@@ -127,5 +135,3 @@ mixin(this, webdriverInstance, method, this.waitForAngular.bind(this));

*/
this.$ = function(selector) {
return self.element(webdriver.By.css(selector));
};
this.$ = build$(this.element, webdriver.By);

@@ -137,5 +143,3 @@ /**

*/
this.$$ = function(selector) {
return self.element.all(webdriver.By.css(selector));
};
this.$$ = build$$(this.element, webdriver.By);

@@ -332,3 +336,3 @@ /**

Protractor.prototype.isElementPresent = function(locatorOrElement) {
var element = (locatorOrElement instanceof webdriver.promise.Promise) ?
var element = (locatorOrElement instanceof webdriver.promise.Promise) ?
locatorOrElement : this.element(locatorOrElement);

@@ -554,3 +558,3 @@ return element.isPresent();

* @param {string} url In page URL using the same syntax as $location.url()
* @returns {!webdriver.promise.Promise} A promise that will resolve once
* @return {!webdriver.promise.Promise} A promise that will resolve once
* page has been changed.

@@ -610,18 +614,12 @@ */

/**
* Beta (unstable) pause function for debugging webdriver tests. Use
* browser.pause() in your test to enter the protractor debugger from that
* point in the control flow.
* Does not require changes to the command line (no need to add 'debug').
* Note, if you are wrapping your own instance of Protractor, you must
* expose globals 'browser' and 'protractor' for pause to work.
* Helper function to:
* 1) Set up helper functions for debugger clients to call on (e.g.
* getControlFlowText, execute code, get autocompletion).
* 2) Enter process into debugger mode. (i.e. process._debugProcess).
* 3) Invoke the debugger client specified by debuggerClientPath.
*
* @example
* element(by.id('foo')).click();
* browser.pause();
* // Execution will stop before the next click action.
* element(by.id('bar')).click();
*
* @param {string=} debuggerClientPath Absolute path of debugger client to use
* @param {number=} opt_debugPort Optional port to use for the debugging process
*/
Protractor.prototype.pause = function(opt_debugPort) {
Protractor.prototype.initDebugger_ = function(debuggerClientPath, opt_debugPort) {
// Patch in a function to help us visualize what's going on in the control

@@ -635,3 +633,3 @@ // flow.

var stacktrace = frameOrTask.snapshot_.getStacktrace();
stacktrace = stacktrace ? stacktrace.join('\n').trim() : '';
stacktrace = stacktrace ? stacktrace.join('\n').trim() : '';
descriptions.push({

@@ -677,5 +675,5 @@ description: frameOrTask.getDescription(),

log.puts('Starting WebDriver debugger in a child process. Pause is ' +
'still beta, please report issues at github.com/angular/protractor');
'still beta, please report issues at github.com/angular/protractor\n');
var nodedebug = require('child_process').
fork(__dirname + '/debugger/wddebugger.js', [process.debugPort]);
fork(debuggerClientPath, [process.debugPort]);
process.on('exit', function() {

@@ -689,8 +687,8 @@ nodedebug.kill('SIGTERM');

// Helper used only by './debugger/wddebugger.js' to insert code into the
// control flow.
// Helper used only by debuggers at './debugger/modes/*.js' to insert code
// into the control flow.
// In order to achieve this, we maintain a promise at the top of the control
// flow, so that we can insert frames into it.
// To be able to simulate callback/asynchronous code, we poll this object
// for an result at every run of DeferredExecutor.execute.
// To be able to simulate callback/asynchronous code, we poll this object
// for an result at every run of DeferredExecutor.execute.
this.dbgCodeExecutor_ = {

@@ -700,3 +698,3 @@ execPromise_: pausePromise, // Promise pointing to current stage of flow.

execPromiseError_: undefined, // Error from promise.
// A dummy repl server to make use of its completion function.

@@ -708,3 +706,3 @@ replServer_: require('repl').start({

// Execute a function, which could yield a value or a promise,
// Execute a function, which could yield a value or a promise,
// and allow its result to be accessed synchronously

@@ -716,4 +714,10 @@ execute_: function(execFn_) {

self.execPromise_ = self.execPromise_.
then(execFn_).
then(function(result) {
then(function() {
var result = execFn_();
if (webdriver.promise.isPromise(result)) {
return result.then(function(val) {return val;});
} else {
return result;
}
}).then(function(result) {
self.execPromiseResult_ = result;

@@ -736,3 +740,3 @@ }, function(err) {

// Run code through vm so that we can maintain a local scope which is
// isolated from the rest of the execution.
// isolated from the rest of the execution.
return vm_.runInThisContext(code);

@@ -756,9 +760,9 @@ };

// Code finished executing.
// Code finished executing.
resultReady: function() {
return !this.execPromise_.isPending();
},
},
// Get asynchronous results synchronously.
// This will throw if result is not ready.
// Get asynchronous results synchronously.
// This will throw if result is not ready.
getResult: function() {

@@ -771,3 +775,3 @@ if (!this.resultReady()) {

}
return JSON.stringify(this.execPromiseResult_);

@@ -777,2 +781,15 @@ }

global.list = function(locator) {
/* globals browser */
return browser.findElements(locator).then(function(arr) {
var found = [];
for (var i = 0; i < arr.length; ++i) {
arr[i].getText().then(function(text) {
found.push(text);
});
}
return found;
});
};
flow.timeout(1000, 'waiting for debugger to attach');

@@ -782,2 +799,43 @@ };

/**
* Beta (unstable) enterRepl function for entering the repl loop from
* any point in the control flow. Use browser.enterRepl() in your test.
* Does not require changes to the command line (no need to add 'debug').
* Note, if you are wrapping your own instance of Protractor, you must
* expose globals 'browser' and 'protractor' for pause to work.
*
* @example
* element(by.id('foo')).click();
* browser.enterRepl();
* // Execution will stop before the next click action.
* element(by.id('bar')).click();
*
* @param {number=} opt_debugPort Optional port to use for the debugging process
*/
Protractor.prototype.enterRepl = function(opt_debugPort) {
var debuggerClientPath = __dirname + '/debugger/clients/explorer.js';
this.initDebugger_(debuggerClientPath, opt_debugPort);
};
/**
* Beta (unstable) pause function for debugging webdriver tests. Use
* browser.pause() in your test to enter the protractor debugger from that
* point in the control flow.
* Does not require changes to the command line (no need to add 'debug').
* Note, if you are wrapping your own instance of Protractor, you must
* expose globals 'browser' and 'protractor' for pause to work.
*
* @example
* element(by.id('foo')).click();
* browser.pause();
* // Execution will stop before the next click action.
* element(by.id('bar')).click();
*
* @param {number=} opt_debugPort Optional port to use for the debugging process
*/
Protractor.prototype.pause = function(opt_debugPort) {
var debuggerClientPath = __dirname + '/debugger/clients/wddebugger.js';
this.initDebugger_(debuggerClientPath, opt_debugPort);
};
/**
* Create a new instance of Protractor by wrapping a webdriver instance.

@@ -784,0 +842,0 @@ *

@@ -188,5 +188,16 @@ var protractor = require('./protractor'),

}
var self = this;
var self = this;
/**
* Get the processed configuration object that is currently being run. This
* will contain the specs and capabilities properties of the current runner
* instance.
*
* @return {q.Promise} A promise which resolves to the capabilities object.
*/
browser_.getProcessedConfig = function() {
return webdriver.promise.fulfilled(config);
};
/**
* Fork another instance of protractor for use in interactive tests.

@@ -238,3 +249,3 @@ *

if (!this.config_.specs.length) {
if (this.config_.framework !== 'explorer' && !this.config_.specs.length) {
throw new Error('Spec patterns did not match any files.');

@@ -275,3 +286,13 @@ }

} else if (self.config_.framework === 'debugprint') {
// Private framework. Do not use.
frameworkPath = './frameworks/debugprint.js';
} else if (self.config_.framework === 'explorer') {
// Private framework. Do not use.
frameworkPath = './frameworks/explorer.js';
} else if (self.config_.framework === 'custom') {
if (!self.config_.frameworkPath) {
throw new Error('When config.framework is custom, ' +
'config.frameworkPath is required.');
}
frameworkPath = self.config_.frameworkPath;
} else {

@@ -287,3 +308,3 @@ throw new Error('config.framework (' + self.config_.framework +

self.driverprovider_.quitDriver(browser_.driver);
// Copy mock modules, but do not navigate to previous URL.
// Copy mock modules, but do not navigate to previous URL.
browser_ = browser_.forkNewDriverInstance(false, true);

@@ -294,3 +315,3 @@ self.setupGlobals_(browser_);

self.on('testPass', restartDriver);
self.on('testFail', restartDriver);
self.on('testFail', restartDriver);
}

@@ -297,0 +318,0 @@

@@ -56,3 +56,3 @@ /**

process.exit(exitCode);
}).catch(function(err) {
}).catch (function(err) {
log.puts(err.message);

@@ -59,0 +59,0 @@ process.exit(1);

@@ -30,3 +30,3 @@ var EOL = require('os').EOL;

if (this.task.specs.length === 1) {
output += 'Specs: '+ this.task.specs.toString() + EOL + EOL;
output += 'Specs: ' + this.task.specs.toString() + EOL + EOL;
}

@@ -52,5 +52,5 @@ this.log(output);

* Log the data in the argument such that metadata are appended.
* The data will be saved to a buffer until flush() is called.
* The data will be saved to a buffer until flush() is called.
*
* @param {string} data
* @param {string} data
*/

@@ -63,3 +63,3 @@ TaskLogger.prototype.log = function(data) {

tag += (capabilities.version) ?
(' ' + capabilities.version) : '';
(' ' + capabilities.version) : '';
tag += (capabilities.platform) ?

@@ -71,3 +71,3 @@ (' ' + capabilities.platform) : '';

data = data.toString();
for ( var i = 0; i < data.length; i++ ) {
for (var i = 0; i < data.length; i++) {
if (this.insertTag) {

@@ -78,3 +78,3 @@ this.insertTag = false;

// See https://github.com/angular/protractor/pull/1216
if (data[i] === '\x1B' && data.substring(i, i+4) === '\x1B[0m' ) {
if (data[i] === '\x1B' && data.substring(i, i + 4) === '\x1B[0m') {
this.buffer += ('\x1B[0m' + tag);

@@ -84,3 +84,3 @@ i += 3;

}
this.buffer += tag;

@@ -87,0 +87,0 @@ }

@@ -10,3 +10,3 @@ var child = require('child_process');

/**
* A runner for running a specified task (capabilities + specs).
* A runner for running a specified task (capabilities + specs).
* The TaskRunner can either run the task from the current process (via

@@ -34,4 +34,4 @@ * './runner.js') or from a new process (via './runnerCli.js').

* running. The promise contains the following parameters representing the
* result of the run:
* taskId, specs, capabilities, failedCount, exitCode, specResults
* result of the run:
* taskId, specs, capabilities, failedCount, exitCode, specResults
*/

@@ -38,0 +38,0 @@ TaskRunner.prototype.run = function() {

@@ -19,7 +19,7 @@ /**

/**
* A scheduler to keep track of specs that need running and their associated
* capabilities. It will suggest a task (combination of capabilities and spec)
* to run while observing the following config rules:
* A scheduler to keep track of specs that need running and their associated
* capabilities. It will suggest a task (combination of capabilities and spec)
* to run while observing the following config rules:
* multiCapabilities, shardTestFiles, and maxInstance.
* Precondition: multiCapabilities is a non-empty array
* Precondition: multiCapabilities is a non-empty array
* (capabilities and getCapabilities will both be ignored)

@@ -56,4 +56,4 @@ *

var specLists = [];
// If we shard, we return an array of one element arrays, each containing
// the spec file. If we don't shard, we return an one element array
// If we shard, we return an array of one element arrays, each containing
// the spec file. If we don't shard, we return an one element array
// containing an array of all the spec files

@@ -88,3 +88,3 @@ if (capabilities.shardTestFiles) {

var queue = this.taskQueues[rotatedIndex];
if (queue.numRunningInstances < queue.maxInstance &&
if (queue.numRunningInstances < queue.maxInstance &&
queue.specsIndex < queue.specLists.length) {

@@ -120,7 +120,7 @@ this.rotationIndex = rotatedIndex + 1;

TaskScheduler.prototype.numTasksOutstanding = function() {
var count = 0;
var count = 0;
this.taskQueues.forEach(function(queue) {
count += queue.numRunningInstances + (queue.specLists.length - queue.specsIndex);
});
return count;
return count;
};

@@ -137,7 +137,7 @@

} else {
var count = 0;
var count = 0;
this.taskQueues.forEach(function(queue) {
count += Math.min(queue.maxInstance, queue.specLists.length);
});
return count;
return count;
}

@@ -144,0 +144,0 @@ };

var q = require('q'),
path = require('path');
var STACK_SUBSTRINGS_TO_FILTER = [

@@ -45,3 +45,3 @@ 'node_modules/minijasminenode/lib/',

return q.promise(function(resolve) {
if (filenameOrFn &&
if (filenameOrFn &&
!(typeof filenameOrFn === 'string' || typeof filenameOrFn === 'function')) {

@@ -48,0 +48,0 @@ throw 'filenameOrFn must be a string or function';

@@ -56,3 +56,3 @@ {

"license": "MIT",
"version": "1.6.1"
"version": "1.7.0"
}

@@ -56,3 +56,3 @@ var q = require('q'),

this.testProcessSetTimeoutTimestamp = 0;
}
};

@@ -84,3 +84,3 @@ /**

}, referenceStart);
}
};

@@ -118,3 +118,3 @@ /**

// We don't care about the date so just set it to 0.
return Date.parse('01 Jan 1970 ' + event.slice(0, 12));
return Date.parse('01 Jan 1970 ' + event.slice(0, 12));
}

@@ -152,3 +152,3 @@ };

extractCommand: function,
extractTimestamp: function} actions Methods to interpret entries.
extractTimestamp: function} actions Methods to interpret entries.
* @param {number} referenceStart Date in millis.

@@ -198,4 +198,4 @@ */

fs.mkdirSync(this.outdir);
} catch(e) {
if ( e.code != 'EEXIST' ) throw e;
} catch (e) {
if (e.code != 'EEXIST') throw e;
}

@@ -206,3 +206,3 @@ var stream = fs.createReadStream(

fs.writeFileSync(outfile, JSON.stringify(this.timeline));
stream.pipe(fs.createWriteStream(path.join(this.outdir, 'index.html')))
stream.pipe(fs.createWriteStream(path.join(this.outdir, 'index.html')));
stream.on('end', done);

@@ -232,3 +232,3 @@ };

if (!self.testProcessSetTimeoutTimestamp &&
timelineEvent.command.name_ =='setScriptTimeout') {
timelineEvent.command.name_ == 'setScriptTimeout') {
self.testProcessSetTimeoutTimestamp = timelineEvent.start;

@@ -243,3 +243,3 @@ }

};
// Clear the logs here.

@@ -253,3 +253,3 @@ browser.manage().logs().getAvailableLogTypes().then(function(result) {

// deferred.resolve();
// });
// });
} else {

@@ -281,3 +281,3 @@ deferred.resolve();

browser.manage().logs().get('client').then(function(result) {
var serverTimeline =TimelinePlugin.parseArrayLog(
var serverTimeline = TimelinePlugin.parseArrayLog(
result, 'Selenium Client', self.testProcessSetTimeoutTimestamp);

@@ -284,0 +284,0 @@ self.timeline = self.timeline.concat(serverTimeline);

@@ -24,3 +24,2 @@ #!/usr/bin/env node

'node lib/cli.js spec/pluginsFullConf.js',
'node lib/cli.js spec/ngHintSuccessConfig.js',
'node lib/cli.js spec/interactionConf.js',

@@ -31,2 +30,3 @@ 'node lib/cli.js spec/directConnectConf.js',

'node lib/cli.js spec/controlLockConf.js',
'node lib/cli.js spec/customFramework.js',
'node node_modules/.bin/jasmine JASMINE_CONFIG_PATH=scripts/unit_test.json'

@@ -36,5 +36,7 @@ ];

// Plugins
passingTests.push('node node_modules/minijasminenode/bin/minijn ' +
passingTests.push('node node_modules/minijasminenode/bin/minijn ' +
'plugins/timeline/spec/unit.js');
passingTests.push('node lib/cli.js plugins/timeline/spec/conf.js');
passingTests.push(
'node lib/cli.js plugins/timeline/spec/conf.js',
'node lib/cli.js plugins/ngHint/spec/successConfig.js');

@@ -59,3 +61,3 @@ var executor = new Executor();

// assert timeout works
// assert timeout works
executor.addCommandlineTest('node lib/cli.js spec/errorTest/timeoutConf.js')

@@ -104,3 +106,4 @@ .expectExitCode(1)

executor.addCommandlineTest('node lib/cli.js spec/ngHintFailConfig.js')
executor.addCommandlineTest(
'node lib/cli.js plugins/ngHint/spec/failureConfig.js')
.expectExitCode(1)

@@ -107,0 +110,0 @@ .expectErrors([{

@@ -8,3 +8,3 @@ #!/usr/bin/env node

var CommandlineTest = function(command) {
var self = this;
var self = this;
this.command_ = command;

@@ -31,3 +31,3 @@ this.expectedExitCode_ = 0;

// Set the expected exit code for the test command.
// Set the expected exit code for the test command.
this.expectExitCode = function(exitCode) {

@@ -46,3 +46,3 @@ self.expectedExitCode_ = exitCode;

/**
* Add expected error(s) for the test command.
* Add expected error(s) for the test command.
* Input is an object or list of objects of the following form:

@@ -92,9 +92,9 @@ * {

} else {
test_process = child_process.spawn(args[0], args.slice(1), {stdio:'inherit'});
test_process = child_process.spawn(args[0], args.slice(1), {stdio: 'inherit'});
}
test_process.on('error', function(err) {
reject(err);
});
test_process.on('exit', function(exitCode) {

@@ -137,3 +137,3 @@ resolve(exitCode);

if (expectedError.message) {
if (!actualError.errorMsg ||
if (!actualError.errorMsg ||
!actualError.errorMsg.match(new RegExp(expectedError.message))) {

@@ -156,10 +156,10 @@ continue;

if (expectedError.message && expectedError.stackTrace) {
flushAndFail('did not fail with expected error with message: [' +
expectedError.message + '] and stackTrace: [' +
flushAndFail('did not fail with expected error with message: [' +
expectedError.message + '] and stackTrace: [' +
expectedError.stackTrace + ']');
} else if (expectedError.message) {
flushAndFail('did not fail with expected error with message: [' +
flushAndFail('did not fail with expected error with message: [' +
expectedError.message + ']');
} else if (expectedError.stackTrace) {
flushAndFail('did not fail with expected error with stackTrace: [' +
flushAndFail('did not fail with expected error with stackTrace: [' +
expectedError.stackTrace + ']');

@@ -178,8 +178,8 @@ }

&& duration < self.expectedMinTestDuration_) {
flushAndFail('expecting test min duration: ' +
flushAndFail('expecting test min duration: ' +
self.expectedMinTestDuration_ + ', actual: ' + duration);
}
if (self.expectedMaxTestDuration_
if (self.expectedMaxTestDuration_
&& duration > self.expectedMaxTestDuration_) {
flushAndFail('expecting test max duration: ' +
flushAndFail('expecting test max duration: ' +
self.expectedMaxTestDuration_ + ', actual: ' + duration);

@@ -209,3 +209,3 @@ }

tests.push(test);
return test;
return test;
};

@@ -212,0 +212,0 @@

function AnimationCtrl($scope) {
$scope.checked = true;
};
}
AnimationCtrl.$inject = ['$scope'];

@@ -15,2 +15,3 @@ 'use strict';

$routeProvider.when('/interaction', {templateUrl: 'interaction/interaction.html', controller: InteractionCtrl});
$routeProvider.when('/shadow', {templateUrl: 'shadow/shadow.html', controller: ShadowCtrl});
$routeProvider.when('/slowloader', {

@@ -17,0 +18,0 @@ templateUrl: 'polling/polling.html',

@@ -9,3 +9,3 @@ function AsyncCtrl($scope, $http, $timeout, $location) {

$scope.routingChangeStatus = 'not started';
$scope.templateUrl = "/fastTemplateUrl";
$scope.templateUrl = '/fastTemplateUrl';

@@ -16,3 +16,3 @@ $scope.slowHttp = function() {

$scope.slowHttpStatus = 'done';
})
});
};

@@ -26,3 +26,3 @@

$scope.slowFunctionStatus = 'done';
}
};

@@ -69,6 +69,6 @@ $scope.slowTimeout = function() {

$scope.changeTemplateUrl = function() {
$scope.templateUrl = "/slowTemplateUrl";
$scope.templateUrl = '/slowTemplateUrl';
};
};
}
AsyncCtrl.$inject = ['$scope', '$http', '$timeout', '$location'];
function BindingsCtrl($scope) {
$scope.planets = [
{ name: "Mercury",
{ name: 'Mercury',
radius: 1516
},
{ name: "Venus",
{ name: 'Venus',
radius: 3760
},
{ name: "Earth",
{ name: 'Earth',
radius: 3959,
moons: ["Luna"]
moons: ['Luna']
},
{ name: "Mars",
{ name: 'Mars',
radius: 2106,
moons: ["Phobos", "Deimos"]
moons: ['Phobos', 'Deimos']
},
{ name: "Jupiter",
{ name: 'Jupiter',
radius: 43441,
moons: ["Europa", "Io", "Ganymede", "Castillo"]
moons: ['Europa', 'Io', 'Ganymede', 'Castillo']
},
{ name: "Saturn",
{ name: 'Saturn',
radius: 36184,
moons: ["Titan", "Rhea", "Iapetus", "Dione"]
moons: ['Titan', 'Rhea', 'Iapetus', 'Dione']
},
{ name: "Uranus",
{ name: 'Uranus',
radius: 15759,
moons: ["Titania", "Oberon", "Umbriel", "Ariel"]
moons: ['Titania', 'Oberon', 'Umbriel', 'Ariel']
},
{ name: "Neptune",
{ name: 'Neptune',
radius: 15299,
moons: ["Triton", "Proteus", "Nereid", "Larissa"]
moons: ['Triton', 'Proteus', 'Nereid', 'Larissa']
}

@@ -33,0 +33,0 @@ ];

@@ -5,3 +5,3 @@ function ConflictCtrl($scope) {

alsoReused: 'outerbarbaz'
}
};

@@ -11,4 +11,4 @@ $scope.wrapper = [{

alsoReused: 'innerbarbaz'
}]
};
}];
}
ConflictCtrl.$inject = ['$scope'];
function FormCtrl($scope, $window) {
$scope.greeting = "Hiya";
$scope.username = "Anon";
$scope.nickname = "annie";
$scope.aboutbox = "This is a text box";
$scope.color = "blue";
$scope.greeting = 'Hiya';
$scope.username = 'Anon';
$scope.nickname = 'annie';
$scope.aboutbox = 'This is a text box';
$scope.color = 'blue';
$scope.show = true;

@@ -13,3 +13,3 @@

$scope.fruit = '';
$scope.defaultFruit = 'apple'
$scope.defaultFruit = 'apple';
$scope.fruits = ['pear', 'peach', 'banana'];

@@ -19,4 +19,4 @@

$window.alert('Hello');
}
};
}
FormCtrl.$inject = ['$scope', '$window'];
function InteractionCtrl($scope, $interval, $http) {
$scope.messages = [];
$scope.message = "";
$scope.user = "";
$scope.userInput = "";
$scope.message = '';
$scope.user = '';
$scope.userInput = '';
$scope.sendUser = function() {
$scope.user = $scope.userInput;
}
};

@@ -20,3 +20,3 @@ var loadMessages = function() {

});
}
};
var saveMessages = function() {

@@ -26,3 +26,3 @@ var data = {

value: $scope.messages
}
};
$http.post('/storage', data);

@@ -33,3 +33,3 @@ };

$scope.messages.push($scope.user + ': ' + $scope.message);
$scope.message = "";
$scope.message = '';
saveMessages();

@@ -36,0 +36,0 @@ };

@@ -14,3 +14,3 @@ function PollingCtrl($scope, $timeout) {

};
};
}
PollingCtrl.$inject = ['$scope', '$timeout'];

@@ -7,6 +7,6 @@ function RepeaterCtrl($scope) {

{initial: 'Th', name: 'Thursday'},
{initial: 'F', name: 'Friday'},
{initial: 'F', name: 'Friday'}
];
};
}
RepeaterCtrl.$inject = ['$scope'];

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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