[tests][bridge] added source map support / stack trace conversion

This commit is contained in:
Salakar 2018-03-25 21:17:26 +01:00
parent c69c6b9082
commit c9b313f23c
6 changed files with 167 additions and 23 deletions

View File

@ -5,8 +5,12 @@ const rootMap = createCoverageMap({});
module.exports = {
collect() {
if (bridge.context && bridge.context.__coverage__) {
rootMap.merge(Object.assign({}, bridge.context.__coverage__));
global.__coverage__ = rootMap.toJSON();
try {
rootMap.merge(Object.assign({}, bridge.context.__coverage__));
global.__coverage__ = rootMap.toJSON();
} catch (e) {
// ignore
}
}
},
summary() {

View File

@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
global.bridge = {};
require('./source-map');
const detox = require('detox');
const ws = require('./ws');

54
tests-new/bridge/env/node/source-map.js vendored Normal file
View File

@ -0,0 +1,54 @@
/* eslint-disable no-param-reassign */
const Mocha = require('mocha');
const { parse } = require('stacktrace-parser');
const { SourceMapConsumer } = require('source-map');
let bundleFileName = null;
const Runner = Mocha.Runner;
let sourceMapConsumer = null;
const originalFail = Runner.prototype.fail;
/**
* Convert an error frame into a source mapped string
* @param parsed
* @returns {string}
*/
function frameToStr(parsed) {
const { name, line, column, source } = sourceMapConsumer.originalPositionFor({
line: parsed.lineNumber,
column: parsed.column,
});
return ` at ${name || parsed.methodName} (${source ||
parsed.file}:${line || parsed.lineNumber}:${column || parsed.column})`;
}
// override mocha fail so we can replace stack traces
Runner.prototype.fail = function fail(test, error) {
console.dir(error);
const original = error.stack.split('\n');
const parsed = parse(error.stack);
const newStack = [original[0]];
for (let i = 0; i < parsed.length; i++) {
const { file } = parsed[i];
if (file === bundleFileName) newStack.push(frameToStr(parsed[i]));
else newStack.push(original[i + 1]);
}
error.stack = newStack.join('\n');
return originalFail.call(this, test, error);
};
module.exports = {
/**
* Build a source map consumer from source map bundle contents
* @param str
* @param fileName
* @returns {Promise<void>}
*/
async buildSourceMap(str, fileName) {
bundleFileName = fileName;
sourceMapConsumer = await new SourceMapConsumer(str);
},
};

View File

@ -6,13 +6,14 @@ const invariant = require('assert');
const { Script } = require('vm');
const context = require('./context');
const coverage = require('./coverage');
const { buildSourceMap } = require('./source-map');
let send;
let bundle;
const PREPARE = 'prepareJSRuntime';
const BUNDLE_FILE_NAME = 'app.bundle.js';
const EXECUTE = 'executeApplicationScript';
const TEMP_BUNDLE_PATH = '/tmp/bridge/react-native.js';
function reply(id, result) {
send({
@ -25,23 +26,35 @@ function handleError(message) {
throw new Error(message);
}
async function downloadBundle(bundleUrl) {
async function downloadUrl(url) {
const res = await new Promise((resolve, reject) =>
http.get(bundleUrl, resolve).on('error', reject)
http.get(url, resolve).on('error', reject)
);
let buffer = '';
res.setEncoding('utf8');
res.on('data', chunk => (buffer += chunk));
await new Promise(resolve => res.on('end', resolve));
return buffer;
}
bundle = new Script(buffer, {
async function downloadBundle(bundleUrl) {
const bundleStr = await downloadUrl(bundleUrl);
bundle = new Script(bundleStr, {
timeout: 120000,
displayErrors: true,
filename: TEMP_BUNDLE_PATH,
filename: BUNDLE_FILE_NAME,
});
const sourceMapUrl = bundleStr
.slice(bundleStr.lastIndexOf('\n'))
.replace('//# sourceMappingURL=', '');
const sourceMapSource = await downloadUrl(sourceMapUrl);
await buildSourceMap(sourceMapSource, BUNDLE_FILE_NAME);
return bundle;
}
@ -58,6 +71,7 @@ async function getBundle(request) {
parsedUrl.query.inlineSourceMap = true;
delete parsedUrl.search;
console.log(url.format(parsedUrl));
return downloadBundle(url.format(parsedUrl));
}
@ -87,9 +101,8 @@ module.exports = {
global.bridge.context[name] = JSON.parse(request.inject[name]);
}
}
script.runInContext(global.bridge.context, {
filename: TEMP_BUNDLE_PATH,
});
script.runInContext(global.bridge.context, BUNDLE_FILE_NAME);
reply(request.id);
break;
}

View File

@ -576,6 +576,14 @@
"slash": "1.0.0",
"source-map": "0.5.7",
"v8flags": "2.1.1"
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
}
},
"babel-code-frame": {
@ -636,6 +644,13 @@
"private": "0.1.8",
"slash": "1.0.0",
"source-map": "0.5.7"
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
"babel-eslint": {
@ -663,6 +678,13 @@
"lodash": "4.17.5",
"source-map": "0.5.7",
"trim-right": "1.0.1"
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
"babel-helper-builder-binary-assignment-operator-visitor": {
@ -1447,6 +1469,21 @@
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz",
"integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4="
},
"source-map-support": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
"requires": {
"source-map": "0.5.7"
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
}
}
},
@ -2675,6 +2712,14 @@
"is-arrayish": "0.2.1"
}
},
"error-stack-parser": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.1.tgz",
"integrity": "sha1-oyArj7AxFKqbQKDjZp5IsrZaAQo=",
"requires": {
"stackframe": "1.0.4"
}
},
"errorhandler": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.4.3.tgz",
@ -5762,6 +5807,11 @@
"mime-db": "1.23.0"
}
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
},
"statuses": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
@ -5802,6 +5852,13 @@
"integrity": "sha512-12WEgolY5CGvHeHkF5QlM2qatdQC1DyjWkXLK9LzCqzd8YhUZww1+ZCM6E67rJwpeuCU9o1Mkiwd1h7dS+RBvA==",
"requires": {
"source-map": "0.5.7"
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
"micromatch": {
@ -10728,6 +10785,11 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
@ -10773,9 +10835,9 @@
}
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.2.tgz",
"integrity": "sha512-NDJB/R2BS7YJG0tP9SbE4DKwKj1idLT5RJqfVYZ7dreFX7wulZT3xxVhbYKrQo9n0JkRptl51TrX/5VK3HodMA=="
},
"source-map-resolve": {
"version": "0.5.1",
@ -10790,11 +10852,18 @@
}
},
"source-map-support": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.4.tgz",
"integrity": "sha512-PETSPG6BjY1AHs2t64vS2aqAgu6dMIMXJULWFBGbh2Gr8nVLbCFDo6i/RMMvviIQ2h1Z8+5gQhVKSn2je9nmdg==",
"requires": {
"source-map": "0.5.7"
"source-map": "0.6.1"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"source-map-url": {
@ -10864,6 +10933,11 @@
"tweetnacl": "0.14.5"
}
},
"stackframe": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz",
"integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw=="
},
"stacktrace-parser": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.4.tgz",

View File

@ -4,17 +4,13 @@
"private": true,
"scripts": {
"packager-chrome": "node node_modules/react-native/local-cli/cli.js start --platforms ios,android",
"packager-bridge": "REACT_DEBUGGER='echo nope' node node_modules/react-native/local-cli/cli.js start --platforms ios,android",
"packager-bridge": "REACT_DEBUGGER='echo nope' node node_modules/react-native/local-cli/cli.js start --platforms ios,android --reset-cache",
"build-android": "detox build --configuration android.emu.debug",
"test": "npm run test-android && test-ios",
"test-android": "detox test --configuration android.emu.debug",
"test-android-reuse": "detox test --configuration android.emu.debug --reuse",
"test-android-cover": "nyc detox test --configuration android.emu.debug",
"test-android-cover-reuse": "nyc detox test --configuration android.emu.debug --reuse",
"test-ios": "detox test --configuration ios.sim.debug",
"test-ios-cover": "nyc detox test --configuration ios.sim.debug",
"ios:pod:install": "cd ios && rm -rf ReactNativeFirebaseDemo.xcworkspace && pod install && cd .."
@ -33,6 +29,8 @@
"should": "^13.2.1",
"should-sinon": "0.0.6",
"sinon": "^4.4.8",
"source-map": "^0.7.2",
"stacktrace-parser": "^0.1.4",
"ws": "^5.1.0"
},
"devDependencies": {