diff --git a/tests-new/bridge/env/node/ready.js b/tests-new/bridge/env/node/ready.js index 97426352..e2379012 100644 --- a/tests-new/bridge/env/node/ready.js +++ b/tests-new/bridge/env/node/ready.js @@ -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() { diff --git a/tests-new/bridge/env/node/vm.js b/tests-new/bridge/env/node/vm.js index 24de3ea0..dd0c2437 100644 --- a/tests-new/bridge/env/node/vm.js +++ b/tests-new/bridge/env/node/vm.js @@ -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)); + } + } + } + }, +}; diff --git a/tests-new/bridge/env/node/ws.js b/tests-new/bridge/env/node/ws.js index 5c7829c0..aab69dec 100644 --- a/tests-new/bridge/env/node/ws.js +++ b/tests-new/bridge/env/node/ws.js @@ -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)); diff --git a/tests-new/e2e/init.js b/tests-new/e2e/init.js index 6d43f60b..a55ea2da 100755 --- a/tests-new/e2e/init.js +++ b/tests-new/e2e/init.js @@ -14,5 +14,5 @@ after(async () => { }); bridge.beforeContextReset = () => { - console.log('reset'); + // console.dir(bridge.context.__coverage__); };