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

Commit ed6c347

Browse files
committed
chore(mocha): refactor to use selenium-webdriver's mocha adapters
Closes #3985
1 parent f4cf277 commit ed6c347

File tree

1 file changed

+41
-118
lines changed

1 file changed

+41
-118
lines changed

lib/frameworks/mocha.js

Lines changed: 41 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
var q = require('q');
2-
var promise = require('selenium-webdriver').promise;
32

43
/**
54
* Execute the Runner's test cases through Mocha.
@@ -21,14 +20,47 @@ exports.run = function(runner, specs) {
2120
// wait until then to load mocha-webdriver adapters as well.
2221
mocha.suite.on('pre-require', function() {
2322
try {
24-
global.after = wrapped(global.after);
25-
global.afterEach = wrapped(global.afterEach);
26-
global.before = wrapped(global.before);
27-
global.beforeEach = wrapped(global.beforeEach);
28-
29-
global.it = wrapped(global.it);
30-
global.it.only = wrapped(global.iit);
31-
global.it.skip = wrapped(global.xit);
23+
// We need to re-wrap all of the global functions, which `selenium-webdriver/testing` only
24+
// does when it is required. So first we must remove it from the cache.
25+
delete require.cache[require.resolve('selenium-webdriver/testing')];
26+
var seleniumMochaAdapters = require('selenium-webdriver/testing');
27+
28+
// Save unwrapped version
29+
var unwrappedAdapters = {};
30+
['after', 'afterEach', 'before', 'beforeEach', 'it', 'xit', 'iit'].forEach(function(fnName) {
31+
unwrappedAdapters = global[fnName] || Mocha[fnName];
32+
});
33+
34+
var wrapFn = function(seleniumWrappedFn) {
35+
// This does not work on functions that can be nested (e.g. `describe`)
36+
return function() {
37+
// Set globals to unwrapped version to avoid circular reference
38+
var wrappedAdapters = {};
39+
for (var fnName in unwrappedAdapters) {
40+
wrappedAdapters[fnName] = global[fnName];
41+
global[fnName] = unwrappedAdapters[fnName];
42+
}
43+
44+
seleniumWrappedFn.apply(this, arguments);
45+
46+
// Restore wrapped version
47+
for (fnName in wrappedAdapters) {
48+
global[fnName] = wrappedAdapters[fnName];
49+
}
50+
};
51+
};
52+
53+
// Wrap functions
54+
global.after = wrapFn(seleniumMochaAdapters.after);
55+
global.afterEach = wrapFn(seleniumMochaAdapters.afterEach);
56+
global.before = wrapFn(seleniumMochaAdapters.before);
57+
global.beforeEach = wrapFn(seleniumMochaAdapters.beforeEach);
58+
59+
global.it = wrapFn(seleniumMochaAdapters.it);
60+
global.iit = wrapFn(seleniumMochaAdapters.it.only);
61+
global.xit = wrapFn(seleniumMochaAdapters.xit);
62+
global.it.only = wrapFn(seleniumMochaAdapters.it.only);
63+
global.it.skip = wrapFn(seleniumMochaAdapters.it.skip);
3264
} catch (err) {
3365
deferred.reject(err);
3466
}
@@ -97,112 +129,3 @@ exports.run = function(runner, specs) {
97129

98130
return deferred.promise;
99131
};
100-
101-
102-
103-
var flow = (function() {
104-
var initial = process.env['SELENIUM_PROMISE_MANAGER'];
105-
try {
106-
process.env['SELENIUM_PROMISE_MANAGER'] = '1';
107-
return promise.controlFlow();
108-
} finally {
109-
if (initial === undefined) {
110-
delete process.env['SELENIUM_PROMISE_MANAGER'];
111-
} else {
112-
process.env['SELENIUM_PROMISE_MANAGER'] = initial;
113-
}
114-
}
115-
})();
116-
117-
/**
118-
* Wraps a function on Mocha's BDD interface so it runs inside a
119-
* webdriver.promise.ControlFlow and waits for the flow to complete before
120-
* continuing.
121-
* @param {!Function} globalFn The function to wrap.
122-
* @return {!Function} The new function.
123-
*/
124-
function wrapped(globalFn) {
125-
return function() {
126-
if (arguments.length === 1) {
127-
return globalFn(makeAsyncTestFn(arguments[0]));
128-
129-
} else if (arguments.length === 2) {
130-
return globalFn(arguments[0], makeAsyncTestFn(arguments[1]));
131-
132-
} else {
133-
throw Error('Invalid # arguments: ' + arguments.length);
134-
}
135-
};
136-
}
137-
138-
/**
139-
* Wraps a function so that all passed arguments are ignored.
140-
* @param {!Function} fn The function to wrap.
141-
* @return {!Function} The wrapped function.
142-
*/
143-
function seal(fn) {
144-
return function() {
145-
fn();
146-
};
147-
}
148-
149-
/**
150-
* Make a wrapper to invoke caller's test function, fn. Run the test function
151-
* within a ControlFlow.
152-
*
153-
* Should preserve the semantics of Mocha's Runnable.prototype.run (See
154-
* https://github.com/mochajs/mocha/blob/master/lib/runnable.js#L192)
155-
*
156-
* @param {!Function} fn
157-
* @return {!Function}
158-
*/
159-
function makeAsyncTestFn(fn) {
160-
var isAsync = fn.length > 0;
161-
var isGenerator = promise.isGenerator(fn);
162-
if (isAsync && isGenerator) {
163-
throw new TypeError(
164-
'generator-based tests must not take a callback; for async testing,'
165-
+ ' return a promise (or yield on a promise)');
166-
}
167-
168-
var ret = /** @type {function(this: mocha.Context)}*/ function(done) {
169-
var self = this;
170-
var runTest = function(resolve, reject) {
171-
try {
172-
if (self.isAsync) {
173-
fn.call(self, function(err) { err ? reject(err) : resolve(); });
174-
} else if (self.isGenerator) {
175-
resolve(promise.consume(fn, self));
176-
} else {
177-
resolve(fn.call(self));
178-
}
179-
} catch (ex) {
180-
reject(ex);
181-
}
182-
};
183-
184-
if (!promise.USE_PROMISE_MANAGER) {
185-
new promise.Promise(runTest).then(seal(done), done);
186-
return;
187-
}
188-
189-
var runnable = this.runnable();
190-
var mochaCallback = runnable.callback;
191-
runnable.callback = function() {
192-
flow.reset();
193-
return mochaCallback.apply(this, arguments);
194-
};
195-
196-
flow.execute(function controlFlowExecute() {
197-
return new promise.Promise(function(fulfill, reject) {
198-
return runTest(fulfill, reject);
199-
}, flow);
200-
}, runnable.fullTitle()).then(seal(done), done);
201-
};
202-
203-
ret.toString = function() {
204-
return fn.toString();
205-
};
206-
207-
return ret;
208-
}

0 commit comments

Comments
 (0)