[tests] new test infra - start of bridge cleanup

This commit is contained in:
Salakar 2018-03-25 03:34:35 +01:00
parent cbe5d27c2a
commit fcdc63bd74
4 changed files with 95 additions and 237 deletions

View File

@ -1,8 +1,6 @@
/* eslint-disable no-return-assign */
let ready = false;
process.on('bridge-attached', () => {
ready = true;
});
process.on('bridge-attached', () => (ready = true));
module.exports = {
wait() {

View File

@ -1,254 +1,114 @@
/* eslint-disable guard-for-in,no-restricted-syntax,no-return-assign */
const url = require('url');
const http = require('http');
const invariant = require('assert');
const { createContext, Script } = require('vm');
const ws = require('./ws');
const { merge } = require('deeps');
const { Script } = require('vm');
const context = require('./context');
global.bridge.context = null;
global.__coverage__ = {};
let scriptCached = null;
let send;
let bundle;
// this is a dummy file path - without a file name the source map is not used in the vm
const PREPARE = 'prepareJSRuntime';
const EXECUTE = 'executeApplicationScript';
const TEMP_BUNDLE_PATH = '/tmp/bridge/react-native.js';
// TODO -----------------------------------------------------------------------
// TODO -----------------------------------------------------------------------
// TODO -----------------------------------------------------------------------
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO This is just dirty code created just as a proof of concept
// TODO - need to clean it all up / refactor
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO
// TODO -----------------------------------------------------------------------
// TODO -----------------------------------------------------------------------
// TODO -----------------------------------------------------------------------
function reply(id, result) {
send({
replyID: id,
result,
});
}
/**
*
* @param replyId
* @param result
*/
function sendResult(replyID, result) {
ws.send(
JSON.stringify({
replyID,
result,
})
function handleError(message) {
throw new Error(message);
}
async function downloadBundle(bundleUrl) {
const res = await new Promise((resolve, reject) =>
http.get(bundleUrl, resolve).on('error', reject)
);
let buffer = '';
res.setEncoding('utf8');
res.on('data', chunk => (buffer += chunk));
await new Promise(resolve => res.on('end', resolve));
bundle = new Script(buffer, {
timeout: 120000,
displayErrors: true,
filename: TEMP_BUNDLE_PATH,
});
return bundle;
}
/**
* TODO
* @param message
*/
function sendError(error) {
console.log('error');
throw error;
async function getBundle(request) {
if (bundle) return bundle;
console.log('Downloading app bundle...');
const parsedUrl = url.parse(request.url, true);
invariant(parsedUrl.query);
parsedUrl.query.inlineSourceMap = true;
delete parsedUrl.search;
return downloadBundle(url.format(parsedUrl));
}
/**
*
* @param src
* @param callback
*/
function getScript(src, callback) {
if (scriptCached) return callback(null, scriptCached);
return http
.get(src, res => {
let buff = '';
module.exports = {
set send(fn) {
send = fn;
},
res.setEncoding('utf8');
res.on('data', chunk => {
buff += chunk;
});
res.on('end', () => {
scriptCached = new Script(buff, {
// lineOffset: -1,
// columnOffset: -1,
timeout: 120000,
displayErrors: true,
produceCachedData: true,
filename: TEMP_BUNDLE_PATH,
});
callback(null, scriptCached);
});
})
.on('error', err => {
callback(err);
});
}
function consoleShim() {
return {
...console,
log(...args) {
if (
args[0] &&
typeof args[0] === 'string' &&
args[0].startsWith('Running application "')
) {
return;
}
if (
args[0] &&
typeof args[0] === 'string' &&
args[0].startsWith('Deprecated')
) {
return;
}
console.log(...args);
},
warn(...args) {
if (
args[0] &&
typeof args[0] === 'string' &&
args[0].startsWith('Running application "')
) {
return;
}
if (
args[0] &&
typeof args[0] === 'string' &&
args[0].startsWith('Deprecated')
) {
return;
}
console.log(...args);
},
};
}
process.on('ws-message', request => {
// console.log(request.method);
switch (request.method) {
case 'prepareJSRuntime':
if (global.bridge.context) {
// todo __coverage__ not working - mostly empty statements in output
merge(global.__coverage__, global.bridge.context.__coverage__ || {});
try {
for (const name in global.bridge.context.__fbBatchedBridge) {
global.bridge.context.__fbBatchedBridge[name] = undefined;
delete global.bridge.context.__fbBatchedBridge[name];
}
for (const name in global.bridge.context.__fbGenNativeModule) {
global.bridge.context.__fbGenNativeModule[name] = undefined;
delete global.bridge.context.__fbGenNativeModule[name];
}
for (const name in global.bridge.context.__fbBatchedBridgeConfig) {
global.bridge.context.__fbBatchedBridgeConfig[name] = undefined;
delete global.bridge.context.__fbBatchedBridgeConfig[name];
}
for (const name in global.bridge.context) {
global.bridge.context[name] = undefined;
delete global.bridge.context[name];
}
} catch (e) {
console.error(e);
}
}
global.bridge.context = undefined;
global.bridge.context = createContext({
console: consoleShim(),
__bridgeNode: {
ready() {
process.emit('rn-ready');
},
provideReactNativeModule(rnModule) {
global.bridge.rn = undefined;
global.bridge.rn = rnModule;
},
provideModule(moduleExports) {
global.bridge.module = undefined;
global.bridge.module = moduleExports;
},
provideReload(reloadFn) {
global.bridge.reload = undefined;
global.bridge.reload = reloadFn;
},
provideRoot(rootComponent) {
global.bridge.root = undefined;
global.bridge.root = rootComponent;
},
},
});
sendResult(request.id);
return;
case 'executeApplicationScript':
// Modify the URL to make sure we get the inline source map.
// TODO we shouldn't be reparsing if scriptCached is set
const parsedUrl = url.parse(request.url, /* parseQueryString */ true);
invariant(parsedUrl.query);
parsedUrl.query.inlineSourceMap = true;
delete parsedUrl.search;
// $FlowIssue url.format() does not accept what url.parse() returns.
const scriptUrl = url.format(parsedUrl);
getScript(scriptUrl, (err, script) => {
if (err != null) {
sendError(`Failed to get script from packager: ${err.message}`);
return;
}
async message(request) {
const { method } = request;
// console.log(request.method);
switch (method) {
case PREPARE:
await context.cleanup();
context.create();
reply(request.id);
break;
case EXECUTE: {
const script = await getBundle(request);
if (global.bridge.context == null) {
sendError('JS runtime not prepared');
return;
throw new Error('VM context was not prepared.');
}
if (request.inject) {
for (const name in request.inject) {
global.bridge.context[name] = JSON.parse(request.inject[name]);
}
}
try {
script.runInContext(global.bridge.context, TEMP_BUNDLE_PATH);
} catch (e) {
sendError(e);
}
sendResult(request.id);
});
return;
default:
let returnValue = [[], [], [], 0];
try {
if (
global.bridge.context != null &&
typeof global.bridge.context.__fbBatchedBridge === 'object'
) {
returnValue = global.bridge.context.__fbBatchedBridge[
request.method
].apply(null, request.arguments);
}
} catch (e) {
if (request.method !== '$disconnected') {
sendError(
`Failed while making a call ${request.method}:::${e.message}`
);
}
} finally {
sendResult(request.id, JSON.stringify(returnValue));
script.runInContext(global.bridge.context, {
filename: TEMP_BUNDLE_PATH,
});
reply(request.id);
break;
}
}
});
default: {
let returnValue = [[], [], [], 0];
try {
if (
global.bridge.context != null &&
typeof global.bridge.context.__fbBatchedBridge === 'object'
) {
returnValue = global.bridge.context.__fbBatchedBridge[method].apply(
null,
request.arguments
);
}
} catch (e) {
if (method !== '$disconnected') {
handleError(
`Failed while making a call bridge call ${method}::${e.message}`
);
}
} finally {
reply(request.id, JSON.stringify(returnValue));
}
}
}
},
};

View File

@ -5,7 +5,7 @@ const ws = new WebSocket(
'ws://localhost:8081/debugger-proxy?role=debugger&name=Chrome'
);
vm.reply = obj => ws.send(JSON.stringify(obj));
vm.send = obj => ws.send(JSON.stringify(obj));
ws.onmessage = message => vm.message(JSON.parse(message.data));

View File

@ -14,5 +14,5 @@ after(async () => {
});
bridge.beforeContextReset = () => {
console.log('reset');
// console.dir(bridge.context.__coverage__);
};