Skip to content

Commit 90cb13f

Browse files
committed
chore(mocha): refactor to use selenium-webdriver's mocha adapters (angular#4013)
Closes angular#3985
1 parent 41b4fcc commit 90cb13f

File tree

1 file changed

+50
-118
lines changed

1 file changed

+50
-118
lines changed

lib/frameworks/mocha.js

Lines changed: 50 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,56 @@ 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 seleniumAdapter = require('selenium-webdriver/testing');
27+
28+
// Save unwrapped version
29+
var unwrappedFns = {};
30+
['after', 'afterEach', 'before', 'beforeEach', 'it', 'xit', 'iit'].forEach(function(fnName) {
31+
unwrappedFns[fnName] = global[fnName] || Mocha[fnName];
32+
});
33+
34+
var wrapFn = function(seleniumWrappedFn, opt_fnName) {
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 wrappedFns = {};
39+
for (var fnName in unwrappedFns) {
40+
wrappedFns[fnName] = global[fnName];
41+
global[fnName] = unwrappedFns[fnName];
42+
}
43+
44+
var args = arguments;
45+
// Allow before/after hooks to use names
46+
if (opt_fnName && (arguments.length > 1) && (seleniumWrappedFn.length < 2)) {
47+
global[opt_fnName] = global[opt_fnName].bind(this, args[0]);
48+
args = Array.prototype.slice.call(arguments, 1);
49+
}
50+
51+
try {
52+
seleniumWrappedFn.apply(this, args);
53+
} finally {
54+
// Restore wrapped version
55+
for (fnName in wrappedFns) {
56+
global[fnName] = wrappedFns[fnName];
57+
}
58+
}
59+
};
60+
};
61+
62+
// Wrap functions
63+
global.after = wrapFn(seleniumAdapter.after, 'after');
64+
global.afterEach = wrapFn(seleniumAdapter.afterEach, 'afterEach');
65+
global.before = wrapFn(seleniumAdapter.before, 'before');
66+
global.beforeEach = wrapFn(seleniumAdapter.beforeEach, 'beforeEach');
67+
68+
global.it = wrapFn(seleniumAdapter.it);
69+
global.iit = wrapFn(seleniumAdapter.it.only);
70+
global.xit = wrapFn(seleniumAdapter.xit);
71+
global.it.only = wrapFn(seleniumAdapter.it.only);
72+
global.it.skip = wrapFn(seleniumAdapter.it.skip);
3273
} catch (err) {
3374
deferred.reject(err);
3475
}
@@ -97,112 +138,3 @@ exports.run = function(runner, specs) {
97138

98139
return deferred.promise;
99140
};
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)