Skip to content

Commit 1decc7d

Browse files
committed
[js] Defer wrapping the global mocha functions until first use
This prevents a potential reference error when users run with a --hook that loads `selenium-webriver/testing`. This creates a cycle with mocha and the expected globals won't exist when the module runs. Also adding logic to fail with a more informative message when the expected globals are not present. Fixes #2787
1 parent f7d5638 commit 1decc7d

File tree

2 files changed

+126
-15
lines changed

2 files changed

+126
-15
lines changed

javascript/node/selenium-webdriver/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
To use Safari 9 or older, users will have to use an older version of Selenium.
88

9+
* Fixed potential reference errors in `selenium-webdriver/testing` when users
10+
create a cycle with mocha by running with mocha's `--hook` flag.
11+
912

1013
### API Changes
1114

javascript/node/selenium-webdriver/testing/index.js

Lines changed: 123 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
* function maybe() { return Math.random() < 0.5; }
6767
*/
6868

69+
'use strict';
70+
6971
var promise = require('..').promise;
7072
var flow = promise.controlFlow();
7173

@@ -196,6 +198,45 @@ function ignore(predicateFn) {
196198
}
197199

198200

201+
/**
202+
* @param {string} name
203+
* @return {!Function}
204+
* @throws {TypeError}
205+
*/
206+
function getMochaGlobal(name) {
207+
let fn = global[name];
208+
let type = typeof fn;
209+
if (type !== 'function') {
210+
throw TypeError(
211+
`Expected global.${name} to be a function, but is ${type}. `
212+
+ 'This can happen if you try using this module when running '
213+
+ 'with node directly instead of using the mocha executable');
214+
}
215+
return fn;
216+
}
217+
218+
219+
const WRAPPED = {
220+
after: null,
221+
afterEach: null,
222+
before: null,
223+
beforeEach: null,
224+
it: null,
225+
itOnly: null,
226+
xit: null
227+
};
228+
229+
230+
function wrapIt() {
231+
if (!WRAPPED.it) {
232+
let it = getMochaGlobal('it');
233+
WRAPPED.it = wrapped(it);
234+
WRAPPED.itOnly = wrapped(it.only);
235+
}
236+
}
237+
238+
239+
199240
// PUBLIC API
200241

201242

@@ -211,67 +252,134 @@ exports.controlFlow = function(){
211252
/**
212253
* Registers a new test suite.
213254
* @param {string} name The suite name.
214-
* @param {function()=} fn The suite function, or {@code undefined} to define
255+
* @param {function()=} opt_fn The suite function, or `undefined` to define
256+
* a pending test suite.
257+
*/
258+
exports.describe = function(name, opt_fn) {
259+
let fn = getMochaGlobal('describe');
260+
return opt_fn ? fn(name, opt_fn) : fn(name);
261+
};
262+
263+
264+
/**
265+
* Defines a suppressed test suite.
266+
* @param {string} name The suite name.
267+
* @param {function()=} opt_fn The suite function, or `undefined` to define
215268
* a pending test suite.
216269
*/
217-
exports.describe = global.describe;
270+
exports.describe.skip = function(name, opt_fn) {
271+
let fn = getMochaGlobal('describe');
272+
return opt_fn ? fn.skip(name, opt_fn) : fn.skip(name);
273+
};
274+
218275

219276
/**
220277
* Defines a suppressed test suite.
221278
* @param {string} name The suite name.
222-
* @param {function()=} fn The suite function, or {@code undefined} to define
279+
* @param {function()=} opt_fn The suite function, or `undefined` to define
223280
* a pending test suite.
224281
*/
225-
exports.xdescribe = global.xdescribe;
226-
exports.describe.skip = global.describe.skip;
282+
exports.xdescribe = function(name, opt_fn) {
283+
let fn = getMochaGlobal('xdescribe');
284+
return opt_fn ? fn(name, opt_fn) : fn(name);
285+
};
286+
227287

228288
/**
229289
* Register a function to call after the current suite finishes.
230290
* @param {function()} fn .
231291
*/
232-
exports.after = wrapped(global.after);
292+
exports.after = function(fn) {
293+
if (!WRAPPED.after) {
294+
WRAPPED.after = wrapped(getMochaGlobal('after'));
295+
}
296+
WRAPPED.after(fn);
297+
};
298+
233299

234300
/**
235301
* Register a function to call after each test in a suite.
236302
* @param {function()} fn .
237303
*/
238-
exports.afterEach = wrapped(global.afterEach);
304+
exports.afterEach = function(fn) {
305+
if (!WRAPPED.afterEach) {
306+
WRAPPED.afterEach = wrapped(getMochaGlobal('afterEach'));
307+
}
308+
WRAPPED.afterEach(fn);
309+
};
310+
239311

240312
/**
241313
* Register a function to call before the current suite starts.
242314
* @param {function()} fn .
243315
*/
244-
exports.before = wrapped(global.before);
316+
exports.before = function(fn) {
317+
if (!WRAPPED.before) {
318+
WRAPPED.before = wrapped(getMochaGlobal('before'));
319+
}
320+
WRAPPED.before(fn);
321+
};
245322

246323
/**
247324
* Register a function to call before each test in a suite.
248325
* @param {function()} fn .
249326
*/
250-
exports.beforeEach = wrapped(global.beforeEach);
327+
exports.beforeEach = function(fn) {
328+
if (!WRAPPED.beforeEach) {
329+
WRAPPED.beforeEach = wrapped(getMochaGlobal('beforeEach'));
330+
}
331+
WRAPPED.beforeEach(fn);
332+
};
251333

252334
/**
253335
* Add a test to the current suite.
254336
* @param {string} name The test name.
255-
* @param {function()=} fn The test function, or {@code undefined} to define
337+
* @param {function()=} opt_fn The test function, or `undefined` to define
256338
* a pending test case.
257339
*/
258-
exports.it = wrapped(global.it);
340+
exports.it = function(name, opt_fn) {
341+
wrapIt();
342+
if (opt_fn) {
343+
WRAPPED.it(name, opt_fn);
344+
} else {
345+
WRAPPED.it(name);
346+
}
347+
};
259348

260349
/**
261350
* An alias for {@link #it()} that flags the test as the only one that should
262351
* be run within the current suite.
263352
* @param {string} name The test name.
264-
* @param {function()=} fn The test function, or {@code undefined} to define
353+
* @param {function()=} opt_fn The test function, or `undefined` to define
265354
* a pending test case.
266355
*/
267-
exports.iit = exports.it.only = wrapped(global.it.only);
356+
exports.it.only = function(name, opt_fn) {
357+
wrapIt();
358+
if (opt_fn) {
359+
WRAPPED.itOnly(name, opt_fn);
360+
} else {
361+
WRAPPED.itOnly(name);
362+
}
363+
};
364+
268365

269366
/**
270367
* Adds a test to the current suite while suppressing it so it is not run.
271368
* @param {string} name The test name.
272-
* @param {function()=} fn The test function, or {@code undefined} to define
369+
* @param {function()=} opt_fn The test function, or `undefined` to define
273370
* a pending test case.
274371
*/
275-
exports.xit = exports.it.skip = wrapped(global.xit);
372+
exports.xit = function(name, opt_fn) {
373+
if (!WRAPPED.xit) {
374+
WRAPPED.xit = wrapped(getMochaGlobal('xit'));
375+
}
376+
if (opt_fn) {
377+
WRAPPED.xit(name, opt_fn);
378+
} else {
379+
WRAPPED.xit(name);
380+
}
381+
};
382+
276383

384+
exports.it.skip = exports.xit;
277385
exports.ignore = ignore;

0 commit comments

Comments
 (0)