Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit 3d98a16

Browse files
authored
feat(config): Support setting SELENIUM_PROMISE_MANAGER flag via the config (#4023)
Closes #3691
1 parent 4e40fb1 commit 3d98a16

11 files changed

+235
-5
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ selenium/
1313
# Build artifacts
1414

1515
built/
16+
spec/built/
1617
node_modules/
1718
website/bower_components/
1819
website/build/

.jshintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./spec/built/*

gulpfile.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ gulp.task('checkVersion', function(done) {
6060
});
6161

6262
gulp.task('built:copy', function(done) {
63-
return gulp.src(['lib/**/*.js','lib/index.d.ts'])
63+
return gulp.src(['lib/**/*.js'])
6464
.pipe(gulp.dest('built/'));
6565
done();
6666
});
@@ -94,14 +94,17 @@ gulp.task('tsc', function(done) {
9494
runSpawn(done, 'node', ['node_modules/typescript/bin/tsc']);
9595
});
9696

97+
gulp.task('tsc:spec', function(done) {
98+
runSpawn(done, 'node', ['node_modules/typescript/bin/tsc', '-p', 'ts_spec_config.json']);
99+
});
100+
97101
gulp.task('prepublish', function(done) {
98-
runSequence('checkVersion', 'jshint', 'tsc',
99-
'built:copy', done);
102+
runSequence('checkVersion', 'jshint', 'tsc', 'built:copy', 'tsc:spec', done);
100103
});
101104

102105
gulp.task('pretest', function(done) {
103106
runSequence('checkVersion',
104-
['webdriver:update', 'jshint', 'tslint', 'format'], 'tsc', 'built:copy', done);
107+
['webdriver:update', 'jshint', 'tslint', 'format'], 'tsc', 'built:copy', 'tsc:spec', done);
105108
});
106109

107110
gulp.task('default',['prepublish']);

lib/config.ts

+23
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,29 @@ export interface Config {
601601
*/
602602
disableChecks?: boolean;
603603

604+
/**
605+
* Enable/disable the WebDriver Control Flow.
606+
*
607+
* WebDriverJS (and by extention, Protractor) uses a Control Flow to manage the order in which
608+
* commands are executed and promises are resolved (see docs/control-flow.md for details).
609+
* However, as syntax like `async`/`await` are being introduced, WebDriverJS has decided to
610+
* deprecate the control flow, and have users manage the asynchronous activity themselves
611+
* (details here: https://github.com/SeleniumHQ/selenium/issues/2969).
612+
*
613+
* At the moment, the WebDriver Control Flow is still enabled by default. You can disable it by
614+
* setting the environment variable `SELENIUM_PROMISE_MANAGER` to `0`. In a webdriver release in
615+
* Q4 2017, the Control Flow will be disabled by default, but you will be able to re-enable it by
616+
* setting `SELENIUM_PROMISE_MANAGER` to `1`. At a later point, the control flow will be removed
617+
* for good.
618+
*
619+
* If you don't like managing environment variables, you can set this option in your config file,
620+
* and Protractor will handle enabling/disabling the control flow for you. Setting this option
621+
* is higher priority than the `SELENIUM_PROMISE_MANAGER` environment variable.
622+
*
623+
* @type {boolean=}
624+
*/
625+
SELENIUM_PROMISE_MANAGER?: boolean;
626+
604627
seleniumArgs?: any[];
605628
jvmArgs?: string[];
606629
configDir?: string;

lib/runner.ts

+4
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ export class Runner extends EventEmitter {
343343
throw new Error('Spec patterns did not match any files.');
344344
}
345345

346+
if (this.config_.SELENIUM_PROMISE_MANAGER != null) {
347+
(wdpromise as any).USE_PROMISE_MANAGER = this.config_.SELENIUM_PROMISE_MANAGER;
348+
}
349+
346350
// 0) Wait for debugger
347351
return q(this.ready_)
348352
.then(() => {

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"@types/chalk": "^0.4.28",
3333
"@types/glob": "^5.0.29",
3434
"@types/jasmine": "^2.5.38",
35+
"@types/jasminewd2": "^2.0.0",
3536
"@types/minimatch": "^2.0.28",
3637
"@types/minimist": "^1.1.28",
3738
"@types/optimist": "^0.0.29",

scripts/test.js

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var passingTests = [
3838
'node built/cli.js spec/noGlobalsConf.js',
3939
'node built/cli.js spec/angular2Conf.js',
4040
'node built/cli.js spec/hybridConf.js',
41+
'node built/cli.js spec/built/noCFSmokeConf.js',
4142
'node scripts/driverProviderAttachSession.js',
4243
'node scripts/errorTest.js',
4344
// Interactive Element Explorer tasks

spec/ts/noCF/smoke_spec.ts

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Based off of spec/basic/elements_spec.js
2+
import {promise as ppromise, browser, element, by, By, $, $$, ExpectedConditions, ElementFinder} from '../../..';
3+
4+
describe('ElementFinder', function() {
5+
it('should return the same result as browser.findElement', async function() {
6+
await browser.get('index.html#/form');
7+
const nameByElement = element(by.binding('username'));
8+
9+
await expect(nameByElement.getText()).toEqual(
10+
browser.findElement(by.binding('username')).getText());
11+
});
12+
13+
it('should wait to grab the WebElement until a method is called', async function() {
14+
// These should throw no error before a page is loaded.
15+
const usernameInput = element(by.model('username'));
16+
const name = element(by.binding('username'));
17+
18+
await browser.get('index.html#/form');
19+
20+
await expect(name.getText()).toEqual('Anon');
21+
22+
await usernameInput.clear();
23+
await usernameInput.sendKeys('Jane');
24+
await expect(name.getText()).toEqual('Jane');
25+
});
26+
27+
it('should chain element actions', async function() {
28+
await browser.get('index.html#/form');
29+
30+
const usernameInput = element(by.model('username'));
31+
const name = element(by.binding('username'));
32+
33+
await expect(name.getText()).toEqual('Anon');
34+
35+
await ((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane');
36+
await expect(name.getText()).toEqual('Jane');
37+
});
38+
39+
it('chained call should wait to grab the WebElement until a method is called',
40+
async function() {
41+
// These should throw no error before a page is loaded.
42+
const reused = element(by.id('baz')).
43+
element(by.binding('item.reusedBinding'));
44+
45+
await browser.get('index.html#/conflict');
46+
47+
await expect(reused.getText()).toEqual('Inner: inner');
48+
await expect(reused.isPresent()).toBe(true);
49+
});
50+
51+
it('should differentiate elements with the same binding by chaining',
52+
async function() {
53+
await browser.get('index.html#/conflict');
54+
55+
const outerReused = element(by.binding('item.reusedBinding'));
56+
const innerReused =
57+
element(by.id('baz')).element(by.binding('item.reusedBinding'));
58+
59+
await expect(outerReused.getText()).toEqual('Outer: outer');
60+
await expect(innerReused.getText()).toEqual('Inner: inner');
61+
});
62+
63+
it('should chain deeper than 2', async function() {
64+
// These should throw no error before a page is loaded.
65+
const reused = element(by.css('body')).element(by.id('baz')).
66+
element(by.binding('item.reusedBinding'));
67+
68+
await browser.get('index.html#/conflict');
69+
70+
await expect(reused.getText()).toEqual('Inner: inner');
71+
});
72+
73+
it('should allow handling errors', async function() {
74+
await browser.get('index.html#/form');
75+
try {
76+
await $('.nopenopenope').getText();
77+
78+
// The above line should have throw an error. Fail.
79+
await expect(true).toEqual(false);
80+
} catch (e) {
81+
await expect(true).toEqual(true);
82+
}
83+
});
84+
85+
it('should allow handling chained errors', async function() {
86+
await browser.get('index.html#/form');
87+
try {
88+
await $('.nopenopenope').$('furthernope').getText();
89+
90+
// The above line should have throw an error. Fail.
91+
await expect(true).toEqual(false);
92+
} catch (e) {
93+
await expect(true).toEqual(true);
94+
}
95+
});
96+
97+
it('should keep a reference to the original locator', async function() {
98+
await browser.get('index.html#/form');
99+
100+
const byCss = by.css('body');
101+
const byBinding = by.binding('greet');
102+
103+
await expect(element(byCss).locator()).toEqual(byCss);
104+
await expect(element(byBinding).locator()).toEqual(byBinding);
105+
});
106+
107+
it('should propagate exceptions', async function() {
108+
await browser.get('index.html#/form');
109+
110+
const invalidElement = element(by.binding('INVALID'));
111+
const successful = invalidElement.getText().then(function() {
112+
return true;
113+
} as any as (() => ppromise.Promise<void>), function() {
114+
return false;
115+
} as any as (() => ppromise.Promise<void>));
116+
await expect(successful).toEqual(false);
117+
});
118+
119+
it('should be returned from a helper without infinite loops', async function() {
120+
await browser.get('index.html#/form');
121+
const helperPromise = ppromise.when(true).then(function() {
122+
return element(by.binding('greeting'));
123+
});
124+
125+
await helperPromise.then(async function(finalResult: ElementFinder) {
126+
await expect(finalResult.getText()).toEqual('Hiya');
127+
} as any as (() => ppromise.Promise<void>));
128+
});
129+
130+
it('should be usable in WebDriver functions', async function() {
131+
await browser.get('index.html#/form');
132+
const greeting = element(by.binding('greeting'));
133+
await browser.executeScript('arguments[0].scrollIntoView', greeting);
134+
});
135+
136+
it('should allow null as success handler', async function() {
137+
await browser.get('index.html#/form');
138+
139+
const name = element(by.binding('username'));
140+
141+
await expect(name.getText()).toEqual('Anon');
142+
await expect(
143+
name.getText().then(null, function() {})
144+
).toEqual('Anon');
145+
146+
});
147+
148+
it('should check equality correctly', async function() {
149+
await browser.get('index.html#/form');
150+
151+
const usernameInput = element(by.model('username'));
152+
const name = element(by.binding('username'));
153+
154+
await expect(usernameInput.equals(usernameInput)).toEqual(true);
155+
await expect(usernameInput.equals(name)).toEqual(false);
156+
});
157+
});

spec/ts/noCFSmokeConf.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {Config} from '../..';
2+
const env = require('../environment.js');
3+
4+
export let config: Config = {
5+
seleniumAddress: env.seleniumAddress,
6+
7+
framework: 'jasmine',
8+
9+
specs: [
10+
'noCF/smoke_spec.js'
11+
],
12+
13+
capabilities: env.capabilities,
14+
15+
baseUrl: env.baseUrl + '/ng1/',
16+
17+
SELENIUM_PROMISE_MANAGER: false
18+
};

ts_spec_config.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es6",
4+
"module": "commonjs",
5+
"moduleResolution": "node",
6+
"sourceMap": true,
7+
"declaration": true,
8+
"removeComments": false,
9+
"noImplicitAny": true,
10+
"outDir": "spec/built",
11+
"types": [
12+
"jasmine", "jasminewd2", "node",
13+
"chalk", "glob", "minimatch",
14+
"minimist", "optimist", "q",
15+
"selenium-webdriver"
16+
]
17+
},
18+
"include": [
19+
"spec/ts"
20+
]
21+
}

tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"noImplicitAny": true,
1010
"outDir": "built/",
1111
"types": [
12-
"jasmine", "node",
12+
"jasmine", "jasminewd2", "node",
1313
"chalk", "glob", "minimatch",
1414
"minimist", "optimist", "q",
1515
"selenium-webdriver"

0 commit comments

Comments
 (0)