[tests] new test infra - start of bridge cleanup
This commit is contained in:
parent
cbe5d27c2a
commit
fcdc63bd74
|
@ -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() {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -14,5 +14,5 @@ after(async () => {
|
|||
});
|
||||
|
||||
bridge.beforeContextReset = () => {
|
||||
console.log('reset');
|
||||
// console.dir(bridge.context.__coverage__);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue