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

Commit d213aa9

Browse files
committed
deps(selenium): upgrade to selenium 4 (#5095)
- elements workaround for WebElement.equals - added a better unhandled rejection warning message in the launcher - remove global function wrappers for mocha (these wrappers went away with control flow) - fix the attach to session driver provider Typing exported from Protractor: - removed ActionSequence and EventEmitter (actions is currently missing) - removed promise.Promise - removed Promise, defer, delayed, createFlow, controlFlow, all, fulfilled, filter, when Typings exported from WebDriver: - removed attachToSession - removed WebDriver instance methods: touchActions, call - removed WebElement getSize and getLocation for getRect - removed redefined global vars for testing - In the typings, we are missing Options.setScriptTimeout method. This should not impact users unless they are using the driver.manage() method. Tests: - fix element equals test - add missing 'await' in colorList test that is causing unhandled promise rejections. - remove control flow related tests - disable the install test. Installing from "file:../../" is not working. - fix the attach to session driver provider test to exit with a 1 if errors are encountered
1 parent 4672265 commit d213aa9

25 files changed

+116
-183
lines changed

lib/browser.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!';
2121
const DEFAULT_RESET_URL = 'data:text/html,<html></html>';
2222
const DEFAULT_GET_PAGE_TIMEOUT = 10000;
2323

24-
let logger = new Logger('protractor');
24+
let logger = new Logger('browser');
2525

2626
// TODO(cnishina): either remove for loop entirely since this does not export anything
2727
// the user might need since everything is composed (with caveat that this could be a
@@ -489,12 +489,11 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
489489
script = 'return (' + script + ').apply(null, arguments);';
490490
}
491491

492-
// TODO(selenium4): fix promise cast.
493-
return this.driver.schedule(
494-
new Command(CommandName.EXECUTE_SCRIPT)
495-
.setParameter('script', script)
496-
.setParameter('args', scriptArgs),
497-
description) as Promise<any>;
492+
// TODO(selenium4): schedule does not exist on driver. Should use execute instead.
493+
return (this.driver as any)
494+
.execute(new Command(CommandName.EXECUTE_SCRIPT)
495+
.setParameter('script', script)
496+
.setParameter('args', scriptArgs));
498497
}
499498

500499
/**
@@ -512,14 +511,15 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
512511
*/
513512
private executeAsyncScript_(script: string|Function, description: string, ...scriptArgs: any[]):
514513
Promise<any> {
514+
// TODO(selenium4): decide what to do with description.
515515
if (typeof script === 'function') {
516516
script = 'return (' + script + ').apply(null, arguments);';
517517
}
518-
return this.driver.schedule(
519-
new Command(CommandName.EXECUTE_ASYNC_SCRIPT)
520-
.setParameter('script', script)
521-
.setParameter('args', scriptArgs),
522-
description) as Promise<any>;
518+
// TODO(selenium4): fix typings. driver.execute should exist
519+
return (this.driver as any)
520+
.execute(new Command(CommandName.EXECUTE_ASYNC_SCRIPT)
521+
.setParameter('script', script)
522+
.setParameter('args', scriptArgs));
523523
}
524524

525525
/**

lib/driverProviders/attachSession.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* It is responsible for setting up the account object, tearing
44
* it down, and setting up the driver correctly.
55
*/
6-
import {WebDriver} from 'selenium-webdriver';
6+
import {Session, WebDriver} from 'selenium-webdriver';
77

88
import {Config} from '../config';
99
import {Logger} from '../logger';
@@ -38,8 +38,9 @@ export class AttachSession extends DriverProvider {
3838
async getNewDriver(): Promise<WebDriver> {
3939
const httpClient = new http.HttpClient(this.config_.seleniumAddress);
4040
const executor = new http.Executor(httpClient);
41-
const newDriver =
42-
await WebDriver.attachToSession(executor, this.config_.seleniumSessionId, null);
41+
const session = new Session(this.config_.seleniumSessionId, null);
42+
43+
const newDriver = new WebDriver(session, executor);
4344
this.drivers_.push(newDriver);
4445
return newDriver;
4546
}

lib/element.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -1124,11 +1124,13 @@ export class ElementFinder extends WebdriverWebElement {
11241124
* @returns {!Promise<boolean>} A promise that will be
11251125
* resolved to whether the two WebElements are equal.
11261126
*/
1127-
equals(element: ElementFinder|WebElement): Promise<any> {
1128-
return WebElement.equals(
1129-
this.getWebElement(),
1130-
(element as any).getWebElement ? (element as ElementFinder).getWebElement() :
1131-
element as WebElement) as Promise<any>;
1127+
async equals(element: ElementFinder|WebElement): Promise<boolean> {
1128+
const a = await this.getWebElement();
1129+
const b = (element as any).getWebElement ? await(element as ElementFinder).getWebElement() :
1130+
element as WebElement;
1131+
// TODO(selenium4): Use `return WebElement.equals(a, b);` when
1132+
// https://github.com/SeleniumHQ/selenium/pull/6749 is fixed.
1133+
return a.getDriver().executeScript('return arguments[0] === arguments[1]', a, b);
11321134
}
11331135
}
11341136

lib/frameworks/mocha.js

-59
Original file line numberDiff line numberDiff line change
@@ -13,65 +13,6 @@ exports.run = (runner, specs) => {
1313
require('./setupAfterEach').setup(runner, specs);
1414

1515
return new Promise(async (resolve, reject) => {
16-
// Mocha doesn't set up the ui until the pre-require event, so
17-
// wait until then to load mocha-webdriver adapters as well.
18-
mocha.suite.on('pre-require', () => {
19-
try {
20-
// We need to re-wrap all of the global functions, which `selenium-webdriver/testing` only
21-
// does when it is required. So first we must remove it from the cache.
22-
delete require.cache[require.resolve('selenium-webdriver/testing')];
23-
const seleniumAdapter = require('selenium-webdriver/testing');
24-
25-
// Save unwrapped version
26-
let unwrappedFns = {};
27-
['after', 'afterEach', 'before', 'beforeEach', 'it', 'xit', 'iit'].forEach((fnName) => {
28-
unwrappedFns[fnName] = global[fnName] || Mocha[fnName];
29-
});
30-
31-
const wrapFn = (seleniumWrappedFn, opt_fnName) => {
32-
// This does not work on functions that can be nested (e.g. `describe`)
33-
return function() {
34-
// Set globals to unwrapped version to avoid circular reference
35-
let wrappedFns = {};
36-
for (let fnName in unwrappedFns) {
37-
wrappedFns[fnName] = global[fnName];
38-
global[fnName] = unwrappedFns[fnName];
39-
}
40-
41-
let args = arguments;
42-
// Allow before/after hooks to use names
43-
if (opt_fnName && (arguments.length > 1) && (seleniumWrappedFn.length < 2)) {
44-
global[opt_fnName] = global[opt_fnName].bind(this, args[0]);
45-
args = Array.prototype.slice.call(arguments, 1);
46-
}
47-
48-
try {
49-
seleniumWrappedFn.apply(this, args);
50-
} finally {
51-
// Restore wrapped version
52-
for (fnName in wrappedFns) {
53-
global[fnName] = wrappedFns[fnName];
54-
}
55-
}
56-
};
57-
};
58-
59-
// Wrap functions
60-
global.after = wrapFn(seleniumAdapter.after, 'after');
61-
global.afterEach = wrapFn(seleniumAdapter.afterEach, 'afterEach');
62-
global.before = wrapFn(seleniumAdapter.before, 'before');
63-
global.beforeEach = wrapFn(seleniumAdapter.beforeEach, 'beforeEach');
64-
65-
global.it = wrapFn(seleniumAdapter.it);
66-
global.iit = wrapFn(seleniumAdapter.it.only);
67-
global.xit = wrapFn(seleniumAdapter.xit);
68-
global.it.only = wrapFn(seleniumAdapter.it.only);
69-
global.it.skip = wrapFn(seleniumAdapter.it.skip);
70-
} catch (err) {
71-
reject(err);
72-
}
73-
});
74-
7516
mocha.loadFiles();
7617

7718
try {

lib/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {PluginConfig, ProtractorPlugin} from './plugins';
66
import {Ptor} from './ptor';
77

88
// Re-export selenium-webdriver types.
9-
export {ActionSequence, Browser, Builder, Button, Capabilities, Capability, error, EventEmitter, FileDetector, Key, logging, promise, Session, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver';
9+
// TODO(selenium4): Actions class typings missing. ActionSequence is deprecated.
10+
export {/*Actions,*/ Browser, Builder, Button, Capabilities, Capability, error, EventEmitter, FileDetector, Key, logging, promise, Session, until, WebDriver, WebElement, WebElementPromise} from 'selenium-webdriver';
1011
// Re-export public types.
1112
export {ElementHelper, ProtractorBrowser} from './browser';
1213
export {Config} from './config';

lib/launcher.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,14 @@ let initFn = async function(configFile: string, additionalConfig: Config) {
171171
});
172172

173173
process.on('unhandledRejection', (reason: Error | any, p: Promise<any>) => {
174-
logger.warn('Unhandled rejection at:', p, 'reason:', reason);
174+
if (reason.stack.match('angular testability are undefined') ||
175+
reason.stack.match('angular is not defined')) {
176+
logger.warn(
177+
'Unhandled promise rejection error: This is usually occurs ' +
178+
'when a browser.get call is made and a previous async call was ' +
179+
'not awaited');
180+
}
181+
logger.warn(p);
175182
});
176183

177184
process.on('exit', (code: number) => {

lib/runner.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,9 @@ export class Runner extends EventEmitter {
256256
}
257257

258258
await browser_.waitForAngularEnabled(initProperties.waitForAngularEnabled);
259-
await driver.manage().timeouts().setScriptTimeout(initProperties.allScriptsTimeout || 0);
259+
// TODO(selenium4): Options does not have a setScriptTimeout method.
260+
await(driver.manage() as any).setTimeouts({script: initProperties.allScriptsTimeout || 0});
261+
260262

261263
browser_.getProcessedConfig = () => {
262264
return Promise.resolve(config);

package-lock.json

+10-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"jasmine": "^2.8.0",
2020
"optimist": "~0.6.0",
2121
"saucelabs": "^1.5.0",
22-
"selenium-webdriver": "3.6.0",
22+
"selenium-webdriver": "^4.0.0-alpha.1",
2323
"source-map-support": "~0.4.0",
2424
"webdriver-js-extender": "2.1.0",
2525
"webdriver-manager-replacement": "^1.1.1"

scripts/driverProviderAttachSession.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ const run = async () => {
8383
throw new Error('The selenium session was not created.');
8484
}
8585
});
86+
res.on('error', (err) => {
87+
console.log(err);
88+
process.exit(1);
89+
});
8690
});
8791
req.end();
8892
});
@@ -94,7 +98,8 @@ const run = async () => {
9498
console.log(runProtractor.stdout.toString());
9599
if (runProtractor.status !== 0) {
96100
const e = new Error('Protractor did not run properly.');
97-
deleteSession(sessionId, e);
101+
await deleteSession(sessionId, e);
102+
process.exit(1);
98103
}
99104

100105
// 4. After the protractor test completes, check to see that the session still
@@ -120,6 +125,10 @@ const run = async () => {
120125
deleteSession(sessionId, e);
121126
}
122127
});
128+
res.on('error', (err) => {
129+
console.log(err);
130+
process.exit(1);
131+
});
123132
});
124133
req.end();
125134
});

scripts/test.js

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const passingTests = [
5050
// Dependency tests
5151
'node node_modules/jasmine/bin/jasmine.js JASMINE_CONFIG_PATH=scripts/dependency_test.json',
5252
// Typings tests
53+
// TODO(selenium4): consider rewriting this test.
5354
// 'node spec/install/test.js'
5455
];
5556

spec/basic/elements_spec.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const {WebElement} = require('selenium-webdriver');
2+
13
describe('ElementFinder', () => {
24
beforeEach(async() => {
35
// Clear everything between each test.
@@ -162,7 +164,7 @@ describe('ElementFinder', () => {
162164

163165
it('should be returned from a helper without infinite loops', async() => {
164166
await browser.get('index.html#/form');
165-
const helperPromise = protractor.promise.when(true).then(() => {
167+
const helperPromise = Promise.resolve(true).then(() => {
166168
return element(by.binding('greeting'));
167169
});
168170

@@ -385,11 +387,11 @@ describe('ElementArrayFinder', () => {
385387

386388
it('should get an element from an array by promise index', async() => {
387389
const colorList = element.all(by.model('color'));
388-
const index = protractor.promise.fulfilled(1);
390+
const index = Promise.resolve(1);
389391

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

392-
expect(await colorList.get(index).getAttribute('value')).toEqual('green');
394+
expect(await colorList.get(await index).getAttribute('value')).toEqual('green');
393395
});
394396

395397
it('should get an element from an array using negative indices', async() => {

spec/basic/lib_spec.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ describe('protractor library', () => {
2020

2121
it('should export other webdriver classes onto the global protractor',
2222
() => {
23-
expect(protractor.ActionSequence).toBeDefined();
23+
// TODO(selenium4): Actions API missing from typings.
24+
// expect(protractor.Actions).toBeDefined();
2425
expect(protractor.Key.RETURN).toEqual('\uE006');
2526
});
2627

spec/dependencyTest/protractor_spec.js

+5-9
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ describe('require(\'protractor\')', () => {
1313
});
1414

1515
it('should have selenium-webdriver functions defined', () => {
16-
var seleniumFunctions = ['ActionSequence', 'Builder',
17-
'Capabilities', 'Command', 'EventEmitter', 'FileDetector',
18-
'Session', 'WebDriver', 'WebElement', 'WebElementPromise'];
16+
// TODO(selenium4): Update Actions when it is in typings. ActionSequence and EventEmitter is deprecated.
17+
var seleniumFunctions = [/*'Actions', */'Builder',
18+
'Capabilities', 'Command', 'FileDetector', 'Session', 'WebDriver',
19+
'WebElement', 'WebElementPromise'];
1920
for (var pos in seleniumFunctions) {
2021
var propertyObj = seleniumFunctions[pos];
2122
expect(typeof protractor[propertyObj]).toEqual('function');
@@ -31,10 +32,6 @@ describe('require(\'protractor\')', () => {
3132
});
3233

3334

34-
it('should have selenium-webdriver promise.Promise', function() {
35-
expect(typeof protractor['promise']['Promise']).toEqual('function');
36-
});
37-
3835
describe('browser class', () => {
3936
it('should have static variables defined', () => {
4037
var staticVariables = ['By'];
@@ -48,8 +45,7 @@ describe('require(\'protractor\')', () => {
4845

4946
describe('promise namespace', () => {
5047
it('should have functions defined (spot check)', () => {
51-
var promiseFunctions = ['Promise', 'defer', 'delayed', 'createFlow',
52-
'controlFlow', 'all', 'fulfilled', 'filter', 'when' ]
48+
var promiseFunctions = ['delayed', 'filter'];
5349
for (var pos in promiseFunctions) {
5450
var property = promiseFunctions[pos];
5551
expect(typeof protractor.promise[property]).toEqual('function');

0 commit comments

Comments
 (0)