Skip to content

Commit

Permalink
feat(config): Support setting SELENIUM_PROMISE_MANAGER flag via the…
Browse files Browse the repository at this point in the history
… config

Closes angular#3691
  • Loading branch information
sjelin committed Jan 27, 2017
1 parent 77525af commit 45c42b0
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ selenium/
# Build artifacts

built/
spec/built/
node_modules/
website/bower_components/
website/build/
Expand Down
1 change: 1 addition & 0 deletions .jshintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./spec/built/*
11 changes: 7 additions & 4 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ gulp.task('checkVersion', function(done) {
});

gulp.task('built:copy', function(done) {
return gulp.src(['lib/**/*.js','lib/index.d.ts'])
return gulp.src(['lib/**/*.js'])
.pipe(gulp.dest('built/'));
done();
});
Expand Down Expand Up @@ -94,14 +94,17 @@ gulp.task('tsc', function(done) {
runSpawn(done, 'node', ['node_modules/typescript/bin/tsc']);
});

gulp.task('tsc:spec', function(done) {
runSpawn(done, 'node', ['node_modules/typescript/bin/tsc', '-p', 'ts_spec_config.json']);
});

gulp.task('prepublish', function(done) {
runSequence('checkVersion', 'jshint', 'tsc',
'built:copy', done);
runSequence('checkVersion', 'jshint', 'tsc', 'built:copy', 'tsc:spec', done);
});

gulp.task('pretest', function(done) {
runSequence('checkVersion',
['webdriver:update', 'jshint', 'tslint', 'format'], 'tsc', 'built:copy', done);
['webdriver:update', 'jshint', 'tslint', 'format'], 'tsc', 'built:copy', 'tsc:spec', done);
});

gulp.task('default',['prepublish']);
23 changes: 23 additions & 0 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,29 @@ export interface Config {
*/
disableChecks?: boolean;

/**
* Enable/disable the WebDriver Control Flow.
*
* WebDriverJS (and by extention, Protractor) uses a Control Flow to manage the order in which
* commands are executed and promises are resolved (see docs/control-flow.md for details).
* However, as syntax like `async`/`await` are being introduced, WebDriverJS has decided to
* deprecate the control flow, and have users manage the asynchronous activity themselves
* (details here: https://github.com/SeleniumHQ/selenium/issues/2969).
*
* At the moment, by default, the WebDriver control flow is still enabled. You can disable it by
* setting the environment variable `SELENIUM_PROMISE_MANAGER` to `0`. In about one year, the
* control flow will be disabled by default, and you will be able to re-enable it by setting
* `SELENIUM_PROMISE_MANAGER` to `1`. In about two years, the control flow will be removed for
* good.
*
* If you don't like managing environment variables, you can set this option in your config file,
* and Protractor will handle enabling/disabling the control flow for you. Setting this option
* will override the `SELENIUM_PROMISE_MANAGER` environment variable.
*
* @type {boolean=}
*/
SELENIUM_PROMISE_MANAGER?: boolean;

seleniumArgs?: any[];
jvmArgs?: string[];
configDir?: string;
Expand Down
4 changes: 4 additions & 0 deletions lib/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ export class Runner extends EventEmitter {
throw new Error('Spec patterns did not match any files.');
}

if (this.config_.SELENIUM_PROMISE_MANAGER != null) {
(wdpromise as any).USE_PROMISE_MANAGER = this.config_.SELENIUM_PROMISE_MANAGER;
}

// 0) Wait for debugger
return q(this.ready_)
.then(() => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@types/chalk": "^0.4.28",
"@types/glob": "^5.0.29",
"@types/jasmine": "^2.5.38",
"@types/jasminewd2": "^2.0.0",
"@types/minimatch": "^2.0.28",
"@types/minimist": "^1.1.28",
"@types/optimist": "^0.0.29",
Expand Down
1 change: 1 addition & 0 deletions scripts/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var passingTests = [
'node built/cli.js spec/noGlobalsConf.js',
'node built/cli.js spec/angular2Conf.js',
'node built/cli.js spec/hybridConf.js',
'node built/cli.js spec/built/noCFSmokeConf.js',
'node scripts/driverProviderAttachSession.js',
'node scripts/errorTest.js',
// Interactive Element Explorer tasks
Expand Down
154 changes: 154 additions & 0 deletions spec/ts/noCF/smoke_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Based off of spec/basic/elements_spec.js
import {promise as ppromise, browser, element, by, By, $, $$, ExpectedConditions, ElementFinder} from '../../..';

describe('ElementFinder', function() {
it('should return the same result as browser.findElement', async function() {
await browser.get('index.html#/form');
const nameByElement = element(by.binding('username'));

await expect(nameByElement.getText()).toEqual(
browser.findElement(by.binding('username')).getText());
});

it('should wait to grab the WebElement until a method is called', async function() {
// These should throw no error before a page is loaded.
const usernameInput = element(by.model('username'));
const name = element(by.binding('username'));

await browser.get('index.html#/form');

await expect(name.getText()).toEqual('Anon');

await usernameInput.clear();
await usernameInput.sendKeys('Jane');
await expect(name.getText()).toEqual('Jane');
});

it('should chain element actions', async function() {
await browser.get('index.html#/form');

const usernameInput = element(by.model('username'));
const name = element(by.binding('username'));

await expect(name.getText()).toEqual('Anon');

await ((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane');
await expect(name.getText()).toEqual('Jane');
});

it('chained call should wait to grab the WebElement until a method is called',
async function() {
// These should throw no error before a page is loaded.
const reused = element(by.id('baz')).
element(by.binding('item.reusedBinding'));

await browser.get('index.html#/conflict');

await expect(reused.getText()).toEqual('Inner: inner');
await expect(reused.isPresent()).toBe(true);
});

it('should differentiate elements with the same binding by chaining',
async function() {
await browser.get('index.html#/conflict');

const outerReused = element(by.binding('item.reusedBinding'));
const innerReused =
element(by.id('baz')).element(by.binding('item.reusedBinding'));

await expect(outerReused.getText()).toEqual('Outer: outer');
await expect(innerReused.getText()).toEqual('Inner: inner');
});

it('should chain deeper than 2', async function() {
// These should throw no error before a page is loaded.
const reused = element(by.css('body')).element(by.id('baz')).
element(by.binding('item.reusedBinding'));

await browser.get('index.html#/conflict');

await expect(reused.getText()).toEqual('Inner: inner');
});

it('should allow handling errors', async function() {
await browser.get('index.html#/form');
await $('.nopenopenope').getText().then(async function(/* string */) {
// This should throw an error. Fail.
await expect(true).toEqual(false);
} as any as (() => ppromise.Promise<void>), async function(/* error */) {
await expect(true).toEqual(true);
} as any as (() => ppromise.Promise<void>));
});

it('should allow handling chained errors', async function() {
await browser.get('index.html#/form');
await $('.nopenopenope').$('furthernope').getText().then(
async function(/* string */) {
// This should throw an error. Fail.
await expect(true).toEqual(false);
} as any as (() => ppromise.Promise<void>), async function(/* error */) {
await expect(true).toEqual(true);
} as any as (() => ppromise.Promise<void>));
});

it('should keep a reference to the original locator', async function() {
await browser.get('index.html#/form');

const byCss = by.css('body');
const byBinding = by.binding('greet');

await expect(element(byCss).locator()).toEqual(byCss);
await expect(element(byBinding).locator()).toEqual(byBinding);
});

it('should propagate exceptions', async function() {
await browser.get('index.html#/form');

const invalidElement = element(by.binding('INVALID'));
const successful = invalidElement.getText().then(function() {
return true;
} as any as (() => ppromise.Promise<void>), function() {
return false;
} as any as (() => ppromise.Promise<void>));
await expect(successful).toEqual(false);
});

it('should be returned from a helper without infinite loops', async function() {
await browser.get('index.html#/form');
const helperPromise = ppromise.when(true).then(function() {
return element(by.binding('greeting'));
});

await helperPromise.then(async function(finalResult: ElementFinder) {
await expect(finalResult.getText()).toEqual('Hiya');
} as any as (() => ppromise.Promise<void>));
});

it('should be usable in WebDriver functions', async function() {
await browser.get('index.html#/form');
const greeting = element(by.binding('greeting'));
await browser.executeScript('arguments[0].scrollIntoView', greeting);
});

it('should allow null as success handler', async function() {
await browser.get('index.html#/form');

const name = element(by.binding('username'));

await expect(name.getText()).toEqual('Anon');
await expect(
name.getText().then(null, function() {})
).toEqual('Anon');

});

it('should check equality correctly', async function() {
await browser.get('index.html#/form');

const usernameInput = element(by.model('username'));
const name = element(by.binding('username'));

await expect(usernameInput.equals(usernameInput)).toEqual(true);
await expect(usernameInput.equals(name)).toEqual(false);
});
});
20 changes: 20 additions & 0 deletions spec/ts/noCFSmokeConf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {Config} from '../..';
const env = require('../environment.js');


// The main suite of Protractor tests.
export let config: Config = {
seleniumAddress: env.seleniumAddress,

framework: 'jasmine',

specs: [
'noCF/smoke_spec.js'
],

capabilities: env.capabilities,

baseUrl: env.baseUrl + '/ng1/',

SELENIUM_PROMISE_MANAGER: false
};
21 changes: 21 additions & 0 deletions ts_spec_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"declaration": true,
"removeComments": false,
"noImplicitAny": true,
"outDir": "spec/built",
"types": [
"jasmine", "jasminewd2", "node",
"chalk", "glob", "minimatch",
"minimist", "optimist", "q",
"selenium-webdriver"
]
},
"include": [
"spec/ts"
]
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"noImplicitAny": true,
"outDir": "built/",
"types": [
"jasmine", "node",
"jasmine", "jasminewd2", "node",
"chalk", "glob", "minimatch",
"minimist", "optimist", "q",
"selenium-webdriver"
Expand Down

0 comments on commit 45c42b0

Please sign in to comment.