Files
react-native/Libraries/BatchedBridge/__tests__/MessageQueue-test.js
T
Ramanpreet Nara 40a86b0650 Add more information to JavaScript module not registered as callable error message
Summary:
When a JavaScript module is not registered as callable, the bridge will throw an error. Let's clean up the error message so that it's more helpful.

Now, it logs the number of registered callable JavaScript modules, as well as the list of callable JavaScript modules.

Changelog: [Internal]

Reviewed By: sshic

Differential Revision: D36646936

fbshipit-source-id: 62d091cda35e296ef9e569b444cd5b6f09b8bc54
2022-05-25 13:21:08 -07:00

166 lines
5.3 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+react_native
* @format
*/
'use strict';
let MessageQueue;
let MessageQueueTestModule;
let queue;
const MODULE_IDS = 0;
const METHOD_IDS = 1;
const PARAMS = 2;
const assertQueue = (flushedQueue, index, moduleID, methodID, params) => {
expect(flushedQueue[MODULE_IDS][index]).toEqual(moduleID);
expect(flushedQueue[METHOD_IDS][index]).toEqual(methodID);
expect(flushedQueue[PARAMS][index]).toEqual(params);
};
// Important things to test:
//
// [x] Local modules can be invoked through the queue.
//
// [ ] Local modules that throw exceptions are gracefully caught. In that case
// local callbacks stored by IDs are cleaned up.
describe('MessageQueue', function () {
beforeEach(function () {
jest.resetModules();
MessageQueue = require('../MessageQueue');
MessageQueueTestModule = require('../__mocks__/MessageQueueTestModule');
queue = new MessageQueue();
queue.registerCallableModule(
'MessageQueueTestModule',
MessageQueueTestModule,
);
queue.createDebugLookup(0, 'MessageQueueTestModule', [
'testHook1',
'testHook2',
]);
});
it('should enqueue native calls', () => {
queue.enqueueNativeCall(0, 1, [2]);
const flushedQueue = queue.flushedQueue();
assertQueue(flushedQueue, 0, 0, 1, [2]);
});
it('should call a local function with the function name', () => {
MessageQueueTestModule.testHook2 = jest.fn();
expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(0);
queue.__callFunction('MessageQueueTestModule', 'testHook2', [2]);
expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(1);
});
it('should store callbacks', () => {
queue.enqueueNativeCall(0, 1, ['foo'], null, null);
const flushedQueue = queue.flushedQueue();
assertQueue(flushedQueue, 0, 0, 1, ['foo']);
});
it('should call the stored callback', () => {
let done = false;
queue.enqueueNativeCall(
0,
1,
[],
() => {},
() => {
done = true;
},
);
queue.__invokeCallback(1, []);
expect(done).toEqual(true);
});
it('should throw when calling the same callback twice', () => {
queue.enqueueNativeCall(
0,
1,
[],
() => {},
() => {},
);
queue.__invokeCallback(1, []);
expect(() => queue.__invokeCallback(1, [])).toThrow();
});
it('should throw when calling both success and failure callback', () => {
queue.enqueueNativeCall(
0,
1,
[],
() => {},
() => {},
);
queue.__invokeCallback(1, []);
expect(() => queue.__invokeCallback(0, [])).toThrow();
});
it('should throw when calling with unknown module', () => {
const unknownModule = 'UnknownModule',
unknownMethod = 'UnknownMethod';
expect(() => queue.__callFunction(unknownModule, unknownMethod)).toThrow(
`Failed to call into JavaScript module method ${unknownModule}.${unknownMethod}(). Module has not been registered as callable. Registered callable JavaScript modules (n = 1): MessageQueueTestModule.
A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.`,
);
});
it('should return lazily registered module', () => {
const dummyModule = {},
name = 'modulesName';
queue.registerLazyCallableModule(name, () => dummyModule);
expect(queue.getCallableModule(name)).toEqual(dummyModule);
});
it('should not initialize lazily registered module before it was used for the first time', () => {
const dummyModule = {},
name = 'modulesName';
const factory = jest.fn(() => dummyModule);
queue.registerLazyCallableModule(name, factory);
expect(factory).not.toHaveBeenCalled();
});
it('should initialize lazily registered module only once', () => {
const dummyModule = {},
name = 'modulesName';
const factory = jest.fn(() => dummyModule);
queue.registerLazyCallableModule(name, factory);
queue.getCallableModule(name);
queue.getCallableModule(name);
expect(factory).toHaveBeenCalledTimes(1);
});
it('should check if the global error handler is not overridden by the DebuggerInternal object', () => {
const dummyModule = {
dummy: function () {},
};
const name = 'emptyModuleName';
const factory = jest.fn(() => dummyModule);
queue.__shouldPauseOnThrow = jest.fn(() => false);
queue.registerLazyCallableModule(name, factory);
queue.callFunctionReturnFlushedQueue(name, 'dummy', []);
expect(queue.__shouldPauseOnThrow).toHaveBeenCalledTimes(2);
});
it('should check if the global error handler is overridden by the DebuggerInternal object', () => {
const dummyModule = {
dummy: function () {},
};
const name = 'emptyModuleName';
const factory = jest.fn(() => dummyModule);
queue.__shouldPauseOnThrow = jest.fn(() => true);
queue.registerLazyCallableModule(name, factory);
queue.callFunctionReturnFlushedQueue(name, 'dummy', []);
expect(queue.__shouldPauseOnThrow).toHaveBeenCalledTimes(2);
});
});