wallet-core/node_modules/source-map-support/test.js

582 lines
18 KiB
JavaScript
Raw Normal View History

2017-05-28 00:38:50 +02:00
require('./source-map-support').install({
emptyCacheBetweenOperations: true // Needed to be able to test for failure
});
var SourceMapGenerator = require('source-map').SourceMapGenerator;
var child_process = require('child_process');
var assert = require('assert');
var fs = require('fs');
function compareLines(actual, expected) {
assert(actual.length >= expected.length, 'got ' + actual.length + ' lines but expected at least ' + expected.length + ' lines');
for (var i = 0; i < expected.length; i++) {
// Some tests are regular expressions because the output format changed slightly between node v0.9.2 and v0.9.3
if (expected[i] instanceof RegExp) {
assert(expected[i].test(actual[i]), JSON.stringify(actual[i]) + ' does not match ' + expected[i]);
} else {
assert.equal(actual[i], expected[i]);
}
}
}
function createEmptySourceMap() {
return new SourceMapGenerator({
file: '.generated.js',
sourceRoot: '.'
});
}
function createSourceMapWithGap() {
var sourceMap = createEmptySourceMap();
sourceMap.addMapping({
generated: { line: 100, column: 0 },
original: { line: 100, column: 0 },
source: '.original.js'
});
return sourceMap;
}
function createSingleLineSourceMap() {
var sourceMap = createEmptySourceMap();
sourceMap.addMapping({
generated: { line: 1, column: 0 },
original: { line: 1, column: 0 },
source: '.original.js'
});
return sourceMap;
}
function createSecondLineSourceMap() {
var sourceMap = createEmptySourceMap();
sourceMap.addMapping({
generated: { line: 2, column: 0 },
original: { line: 1, column: 0 },
source: '.original.js'
});
return sourceMap;
}
function createMultiLineSourceMap() {
var sourceMap = createEmptySourceMap();
for (var i = 1; i <= 100; i++) {
sourceMap.addMapping({
generated: { line: i, column: 0 },
original: { line: 1000 + i, column: 99 + i },
source: 'line' + i + '.js'
});
}
return sourceMap;
}
function createMultiLineSourceMapWithSourcesContent() {
var sourceMap = createEmptySourceMap();
var original = new Array(1001).join('\n');
for (var i = 1; i <= 100; i++) {
sourceMap.addMapping({
generated: { line: i, column: 0 },
original: { line: 1000 + i, column: 4 },
source: 'original.js'
});
original += ' line ' + i + '\n';
}
sourceMap.setSourceContent('original.js', original);
return sourceMap;
}
function compareStackTrace(sourceMap, source, expected) {
// Check once with a separate source map
fs.writeFileSync('.generated.js.map', sourceMap);
fs.writeFileSync('.generated.js', 'exports.test = function() {' +
source.join('\n') + '};//@ sourceMappingURL=.generated.js.map');
try {
delete require.cache[require.resolve('./.generated')];
require('./.generated').test();
} catch (e) {
compareLines(e.stack.split(/\r\n|\n/), expected);
}
fs.unlinkSync('.generated.js');
fs.unlinkSync('.generated.js.map');
// Check again with an inline source map (in a data URL)
fs.writeFileSync('.generated.js', 'exports.test = function() {' +
source.join('\n') + '};//@ sourceMappingURL=data:application/json;base64,' +
new Buffer(sourceMap.toString()).toString('base64'));
try {
delete require.cache[require.resolve('./.generated')];
require('./.generated').test();
} catch (e) {
compareLines(e.stack.split(/\r\n|\n/), expected);
}
fs.unlinkSync('.generated.js');
}
function compareStdout(done, sourceMap, source, expected) {
fs.writeFileSync('.original.js', 'this is the original code');
fs.writeFileSync('.generated.js.map', sourceMap);
fs.writeFileSync('.generated.js', source.join('\n') +
'//@ sourceMappingURL=.generated.js.map');
child_process.exec('node ./.generated', function(error, stdout, stderr) {
try {
compareLines(
(stdout + stderr)
.trim()
.split(/\r\n|\n/)
.filter(function (line) { return line !== '' }), // Empty lines are not relevant.
expected
);
} catch (e) {
return done(e);
}
fs.unlinkSync('.generated.js');
fs.unlinkSync('.generated.js.map');
fs.unlinkSync('.original.js');
done();
});
}
it('normal throw', function() {
compareStackTrace(createMultiLineSourceMap(), [
'throw new Error("test");'
], [
'Error: test',
/^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/
]);
});
it('throw inside function', function() {
compareStackTrace(createMultiLineSourceMap(), [
'function foo() {',
' throw new Error("test");',
'}',
'foo();'
], [
'Error: test',
/^ at foo \((?:.*[/\\])?line2\.js:1002:102\)$/,
/^ at Object\.exports\.test \((?:.*[/\\])?line4\.js:1004:104\)$/
]);
});
it('throw inside function inside function', function() {
compareStackTrace(createMultiLineSourceMap(), [
'function foo() {',
' function bar() {',
' throw new Error("test");',
' }',
' bar();',
'}',
'foo();'
], [
'Error: test',
/^ at bar \((?:.*[/\\])?line3\.js:1003:103\)$/,
/^ at foo \((?:.*[/\\])?line5\.js:1005:105\)$/,
/^ at Object\.exports\.test \((?:.*[/\\])?line7\.js:1007:107\)$/
]);
});
it('eval', function() {
compareStackTrace(createMultiLineSourceMap(), [
'eval("throw new Error(\'test\')");'
], [
'Error: test',
// Before Node 4, `Object.eval`, after just `eval`.
/^ at (?:Object\.)?eval \(eval at (<anonymous>|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/,
/^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/
]);
});
it('eval inside eval', function() {
compareStackTrace(createMultiLineSourceMap(), [
'eval("eval(\'throw new Error(\\"test\\")\')");'
], [
'Error: test',
/^ at (?:Object\.)?eval \(eval at (<anonymous>|exports.test) \(eval at (<anonymous>|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/,
/^ at (?:Object\.)?eval \(eval at (<anonymous>|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/,
/^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/
]);
});
it('eval inside function', function() {
compareStackTrace(createMultiLineSourceMap(), [
'function foo() {',
' eval("throw new Error(\'test\')");',
'}',
'foo();'
], [
'Error: test',
/^ at eval \(eval at foo \((?:.*[/\\])?line2\.js:1002:102\)/,
/^ at foo \((?:.*[/\\])?line2\.js:1002:102\)/,
/^ at Object\.exports\.test \((?:.*[/\\])?line4\.js:1004:104\)$/
]);
});
it('eval with sourceURL', function() {
compareStackTrace(createMultiLineSourceMap(), [
'eval("throw new Error(\'test\')//@ sourceURL=sourceURL.js");'
], [
'Error: test',
/^ at (?:Object\.)?eval \(sourceURL\.js:1:7\)$/,
/^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/
]);
});
it('eval with sourceURL inside eval', function() {
compareStackTrace(createMultiLineSourceMap(), [
'eval("eval(\'throw new Error(\\"test\\")//@ sourceURL=sourceURL.js\')");'
], [
'Error: test',
/^ at (?:Object\.)?eval \(sourceURL\.js:1:7\)$/,
/^ at (?:Object\.)?eval \(eval at (<anonymous>|exports.test) \((?:.*[/\\])?line1\.js:1001:101\)/,
/^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/
]);
});
it('native function', function() {
compareStackTrace(createSingleLineSourceMap(), [
'[1].map(function(x) { throw new Error(x); });'
], [
'Error: 1',
/[/\\].original\.js/,
/at Array\.map \(native\)/
]);
});
it('function constructor', function() {
compareStackTrace(createMultiLineSourceMap(), [
'throw new Function(")");'
], [
'SyntaxError: Unexpected token )',
]);
});
it('throw with empty source map', function() {
compareStackTrace(createEmptySourceMap(), [
'throw new Error("test");'
], [
'Error: test',
/^ at Object\.exports\.test \((?:.*[/\\])?.generated.js:1:34\)$/
]);
});
it('throw in Timeout with empty source map', function(done) {
compareStdout(done, createEmptySourceMap(), [
'require("./source-map-support").install();',
'setTimeout(function () {',
' throw new Error("this is the error")',
'})'
], [
/[/\\].generated.js:3$/,
' throw new Error("this is the error")',
/^ \^$/,
'Error: this is the error',
/^ at ((null)|(Timeout))\._onTimeout \((?:.*[/\\])?.generated.js:3:11\)$/
]);
});
it('throw with source map with gap', function() {
compareStackTrace(createSourceMapWithGap(), [
'throw new Error("test");'
], [
'Error: test',
/^ at Object\.exports\.test \((?:.*[/\\])?.generated.js:1:34\)$/
]);
});
it('sourcesContent with data URL', function() {
compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [
'throw new Error("test");'
], [
'Error: test',
/^ at Object\.exports\.test \((?:.*[/\\])?original.js:1001:5\)$/
]);
});
it('finds the last sourceMappingURL', function() {
compareStackTrace(createMultiLineSourceMapWithSourcesContent(), [
'//# sourceMappingURL=missing.map.js', // NB: compareStackTrace adds another source mapping.
'throw new Error("test");'
], [
'Error: test',
/^ at Object\.exports\.test \((?:.*[/\\])?original.js:1002:5\)$/
]);
});
it('default options', function(done) {
compareStdout(done, createSecondLineSourceMap(), [
'',
'function foo() { throw new Error("this is the error"); }',
'require("./source-map-support").install();',
'process.nextTick(foo);',
'process.nextTick(function() { process.exit(1); });'
], [
/[/\\].original\.js:1$/,
'this is the original code',
'^',
'Error: this is the error',
/^ at foo \((?:.*[/\\])?.original\.js:1:1\)$/
]);
});
it('handleUncaughtExceptions is true', function(done) {
compareStdout(done, createSecondLineSourceMap(), [
'',
'function foo() { throw new Error("this is the error"); }',
'require("./source-map-support").install({ handleUncaughtExceptions: true });',
'process.nextTick(foo);'
], [
/[/\\].original\.js:1$/,
'this is the original code',
'^',
'Error: this is the error',
/^ at foo \((?:.*[/\\])?.original\.js:1:1\)$/
]);
});
it('handleUncaughtExceptions is false', function(done) {
compareStdout(done, createSecondLineSourceMap(), [
'',
'function foo() { throw new Error("this is the error"); }',
'require("./source-map-support").install({ handleUncaughtExceptions: false });',
'process.nextTick(foo);'
], [
/[/\\].generated.js:2$/,
'function foo() { throw new Error("this is the error"); }',
// Before Node 4, the arrow points on the `new`, after on the
// `throw`.
/^ (?: )?\^$/,
'Error: this is the error',
/^ at foo \((?:.*[/\\])?.original\.js:1:1\)$/
]);
});
it('default options with empty source map', function(done) {
compareStdout(done, createEmptySourceMap(), [
'',
'function foo() { throw new Error("this is the error"); }',
'require("./source-map-support").install();',
'process.nextTick(foo);'
], [
/[/\\].generated.js:2$/,
'function foo() { throw new Error("this is the error"); }',
/^ (?: )?\^$/,
'Error: this is the error',
/^ at foo \((?:.*[/\\])?.generated.js:2:24\)$/
]);
});
it('default options with source map with gap', function(done) {
compareStdout(done, createSourceMapWithGap(), [
'',
'function foo() { throw new Error("this is the error"); }',
'require("./source-map-support").install();',
'process.nextTick(foo);'
], [
/[/\\].generated.js:2$/,
'function foo() { throw new Error("this is the error"); }',
/^ (?: )?\^$/,
'Error: this is the error',
/^ at foo \((?:.*[/\\])?.generated.js:2:24\)$/
]);
});
it('specifically requested error source', function(done) {
compareStdout(done, createSecondLineSourceMap(), [
'',
'function foo() { throw new Error("this is the error"); }',
'var sms = require("./source-map-support");',
'sms.install({ handleUncaughtExceptions: false });',
'process.on("uncaughtException", function (e) { console.log("SRC:" + sms.getErrorSource(e)); });',
'process.nextTick(foo);'
], [
/^SRC:.*[/\\].original.js:1$/,
'this is the original code',
'^'
]);
});
it('sourcesContent', function(done) {
compareStdout(done, createMultiLineSourceMapWithSourcesContent(), [
'',
'function foo() { throw new Error("this is the error"); }',
'require("./source-map-support").install();',
'process.nextTick(foo);',
'process.nextTick(function() { process.exit(1); });'
], [
/[/\\]original\.js:1002$/,
' line 2',
' ^',
'Error: this is the error',
/^ at foo \((?:.*[/\\])?original\.js:1002:5\)$/
]);
});
it('missing source maps should also be cached', function(done) {
compareStdout(done, createSingleLineSourceMap(), [
'',
'var count = 0;',
'function foo() {',
' console.log(new Error("this is the error").stack.split("\\n").slice(0, 2).join("\\n"));',
'}',
'require("./source-map-support").install({',
' overrideRetrieveSourceMap: true,',
' retrieveSourceMap: function(name) {',
' if (/\\.generated.js$/.test(name)) count++;',
' return null;',
' }',
'});',
'process.nextTick(foo);',
'process.nextTick(foo);',
'process.nextTick(function() { console.log(count); });',
], [
'Error: this is the error',
/^ at foo \((?:.*[/\\])?.generated.js:4:15\)$/,
'Error: this is the error',
/^ at foo \((?:.*[/\\])?.generated.js:4:15\)$/,
'1', // The retrieval should only be attempted once
]);
});
it('should consult all retrieve source map providers', function(done) {
compareStdout(done, createSingleLineSourceMap(), [
'',
'var count = 0;',
'function foo() {',
' console.log(new Error("this is the error").stack.split("\\n").slice(0, 2).join("\\n"));',
'}',
'require("./source-map-support").install({',
' retrieveSourceMap: function(name) {',
' if (/\\.generated.js$/.test(name)) count++;',
' return undefined;',
' }',
'});',
'require("./source-map-support").install({',
' retrieveSourceMap: function(name) {',
' if (/\\.generated.js$/.test(name)) {',
' count++;',
' return ' + JSON.stringify({url: '.original.js', map: createMultiLineSourceMapWithSourcesContent().toJSON()}) + ';',
' }',
' }',
'});',
'process.nextTick(foo);',
'process.nextTick(foo);',
'process.nextTick(function() { console.log(count); });',
], [
'Error: this is the error',
/^ at foo \((?:.*[/\\])?original.js:1004:5\)$/,
'Error: this is the error',
/^ at foo \((?:.*[/\\])?original.js:1004:5\)$/,
'1', // The retrieval should only be attempted once
]);
});
it('should allow for runtime inline source maps', function(done) {
var sourceMap = createMultiLineSourceMapWithSourcesContent();
fs.writeFileSync('.generated.jss', 'foo');
compareStdout(function(err) {
fs.unlinkSync('.generated.jss');
done(err);
}, createSingleLineSourceMap(), [
'require("./source-map-support").install({',
' hookRequire: true',
'});',
'require.extensions[".jss"] = function(module, filename) {',
' module._compile(',
JSON.stringify([
'',
'var count = 0;',
'function foo() {',
' console.log(new Error("this is the error").stack.split("\\n").slice(0, 2).join("\\n"));',
'}',
'process.nextTick(foo);',
'process.nextTick(foo);',
'process.nextTick(function() { console.log(count); });',
'//@ sourceMappingURL=data:application/json;charset=utf8;base64,' + new Buffer(sourceMap.toString()).toString('base64')
].join('\n')),
', filename);',
'};',
'require("./.generated.jss");',
], [
'Error: this is the error',
/^ at foo \(.*[/\\]original.js:1004:5\)$/,
'Error: this is the error',
/^ at foo \(.*[/\\]original.js:1004:5\)$/,
'0', // The retrieval should only be attempted once
]);
});
/* The following test duplicates some of the code in
* `compareStackTrace` but appends a charset to the
* source mapping url.
*/
it('finds source maps with charset specified', function() {
var sourceMap = createMultiLineSourceMap()
var source = [ 'throw new Error("test");' ];
var expected = [
'Error: test',
/^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/
];
fs.writeFileSync('.generated.js', 'exports.test = function() {' +
source.join('\n') + '};//@ sourceMappingURL=data:application/json;charset=utf8;base64,' +
new Buffer(sourceMap.toString()).toString('base64'));
try {
delete require.cache[require.resolve('./.generated')];
require('./.generated').test();
} catch (e) {
compareLines(e.stack.split(/\r\n|\n/), expected);
}
fs.unlinkSync('.generated.js');
});
/* The following test duplicates some of the code in
* `compareStackTrace` but appends some code and a
* comment to the source mapping url.
*/
it('allows code/comments after sourceMappingURL', function() {
var sourceMap = createMultiLineSourceMap()
var source = [ 'throw new Error("test");' ];
var expected = [
'Error: test',
/^ at Object\.exports\.test \((?:.*[/\\])?line1\.js:1001:101\)$/
];
fs.writeFileSync('.generated.js', 'exports.test = function() {' +
source.join('\n') + '};//# sourceMappingURL=data:application/json;base64,' +
new Buffer(sourceMap.toString()).toString('base64') +
'\n// Some comment below the sourceMappingURL\nvar foo = 0;');
try {
delete require.cache[require.resolve('./.generated')];
require('./.generated').test();
} catch (e) {
compareLines(e.stack.split(/\r\n|\n/), expected);
}
fs.unlinkSync('.generated.js');
});
it('handleUncaughtExceptions is true with existing listener', function(done) {
var source = [
'process.on("uncaughtException", function() { /* Silent */ });',
'function foo() { throw new Error("this is the error"); }',
'require("./source-map-support").install();',
'process.nextTick(foo);',
'//@ sourceMappingURL=.generated.js.map'
];
fs.writeFileSync('.original.js', 'this is the original code');
fs.writeFileSync('.generated.js.map', createSingleLineSourceMap());
fs.writeFileSync('.generated.js', source.join('\n'));
child_process.exec('node ./.generated', function(error, stdout, stderr) {
fs.unlinkSync('.generated.js');
fs.unlinkSync('.generated.js.map');
fs.unlinkSync('.original.js');
assert.equal((stdout + stderr).trim(), '');
done();
});
});