mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 22:13:26 +00:00
Let's say you have module hello.js like so:
// hello.js
const hello_world = i18n.t('Hello world');
exports.get_greeting = () => hello_world;
And then two modules like this:
// apple.js
const hello = require('hello');
exports.foo = () => {
show_greeting(hello.get_greeting());
};
// banana.js
const hello = require('hello');
exports.foo = () => {
display_greeting(hello.get_greeting());
};
The test for apple.js could look like this,
and it won't crash due to the stub:
set_global('i18n', {t: () => {}});
zrequire('hello');
zrequire('apple');
Now let's say your write this broken version
of a test for banana.js:
zrequire('hello');
zrequire('banana');
If you run `./tools/test-js-with-node`, the
"banana" test will pass, because while it
does require "hello", it won't actually
*execute* the code that happens at require
time for "hello", because it's already in
the cache. Here is the code that gets
skipped:
const hello_world = i18n.t('Hello world');
But then if you try to run the banana test
individually, the above line of code will
cause the test to crash. And it will crash
even before you actually try to test the
meaningful code here:
exports.foo = () => {
display_greeting(hello.get_greeting());
};
This commit fixes this leak scenario by just
aggressively clearing out things from the
require cache.
This slows tests down by about 10%, which I think
is worth the extra safety here.
104 lines
2.8 KiB
JavaScript
104 lines
2.8 KiB
JavaScript
const requires = [];
|
|
const new_globals = new Set();
|
|
let old_globals = {};
|
|
|
|
exports.set_global = function (name, val) {
|
|
if (!(name in old_globals)) {
|
|
if (!(name in global)) {
|
|
new_globals.add(name);
|
|
}
|
|
old_globals[name] = global[name];
|
|
}
|
|
global[name] = val;
|
|
return val;
|
|
};
|
|
|
|
exports.zrequire = function (name, fn) {
|
|
if (fn === undefined) {
|
|
fn = '../../static/js/' + name;
|
|
} else if (/^generated\/|^js\/|^shared\/|^third\//.test(fn)) {
|
|
// FIXME: Stealing part of the NPM namespace is confusing.
|
|
fn = '../../static/' + fn;
|
|
}
|
|
delete require.cache[require.resolve(fn)];
|
|
requires.push(fn);
|
|
return require(fn);
|
|
};
|
|
|
|
exports.clear_zulip_refs = function () {
|
|
/*
|
|
This is a big hammer to make sure
|
|
we are not "borrowing" a transitively
|
|
required module from a previous test.
|
|
This kind of leak can make it seems
|
|
like we've written the second test
|
|
correctly, but it will fail if we
|
|
run it standalone.
|
|
*/
|
|
_.each(require.cache, (_, fn) => {
|
|
if (fn.indexOf('static/') >= 0) {
|
|
if (fn.indexOf('static/templates') < 0) {
|
|
delete require.cache[fn];
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
exports.restore = function () {
|
|
requires.forEach(function (fn) {
|
|
delete require.cache[require.resolve(fn)];
|
|
});
|
|
Object.assign(global, old_globals);
|
|
old_globals = {};
|
|
for (const name of new_globals) {
|
|
delete global[name];
|
|
}
|
|
new_globals.clear();
|
|
};
|
|
|
|
exports.stub_out_jquery = function () {
|
|
set_global('$', function () {
|
|
return {
|
|
on: function () {},
|
|
trigger: function () {},
|
|
hide: function () {},
|
|
removeClass: function () {},
|
|
};
|
|
});
|
|
$.fn = {};
|
|
$.now = function () {};
|
|
};
|
|
|
|
exports.with_overrides = function (test_function) {
|
|
// This function calls test_function() and passes in
|
|
// a way to override the namespace temporarily.
|
|
|
|
const clobber_callbacks = [];
|
|
|
|
const override = function (name, f) {
|
|
const parts = name.split('.');
|
|
const module = parts[0];
|
|
const func_name = parts[1];
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(global, module)) {
|
|
set_global(module, {});
|
|
}
|
|
|
|
global[module][func_name] = f;
|
|
|
|
clobber_callbacks.push(function () {
|
|
// If you get a failure from this, you probably just
|
|
// need to have your test do its own overrides and
|
|
// not cherry-pick off of the prior test's setup.
|
|
global[module][func_name] =
|
|
'ATTEMPTED TO REUSE OVERRIDDEN VALUE FROM PRIOR TEST';
|
|
});
|
|
};
|
|
|
|
test_function(override);
|
|
|
|
for (const f of clobber_callbacks) {
|
|
f();
|
|
}
|
|
};
|