/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { DevToolsShim } = ChromeUtils.importESModule( "chrome://devtools-startup/content/DevToolsShim.sys.mjs" ); // Test the DevToolsShim /** * Create a mocked version of DevTools that records all calls made to methods expected * to be called by DevToolsShim. */ function createMockDevTools() { const methods = [ "on", "off", "emit", "saveDevToolsSession", "restoreDevToolsSession", ]; const mock = { callLog: {}, }; for (const method of methods) { // Create a stub for method, that only pushes its arguments in the inner callLog mock[method] = function (...args) { mock.callLog[method].push(args); }; mock.callLog[method] = []; } return mock; } /** * Check if a given method was called an expected number of times, and finally check the * arguments provided to the last call, if appropriate. */ function checkCalls(mock, method, length, lastArgs) { ok( mock.callLog[method].length === length, "Devtools.on was called the expected number of times" ); // If we don't want to check the last call or if the method was never called, bail out. if (!lastArgs || length === 0) { return; } for (let i = 0; i < lastArgs.length; i++) { const expectedArg = lastArgs[i]; ok( mock.callLog[method][length - 1][i] === expectedArg, `Devtools.${method} was called with the expected argument (index ${i})` ); } } function test_register_unregister() { ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); DevToolsShim.register(createMockDevTools()); ok(DevToolsShim.isInitialized(), "DevTools are initialized"); DevToolsShim.unregister(); ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); } function test_on_is_forwarded_to_devtools() { ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); function cb1() {} function cb2() {} const mock = createMockDevTools(); DevToolsShim.on("test_event", cb1); DevToolsShim.register(mock); checkCalls(mock, "on", 1, ["test_event", cb1]); DevToolsShim.on("other_event", cb2); checkCalls(mock, "on", 2, ["other_event", cb2]); } function test_off_called_before_registering_devtools() { ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); function cb1() {} const mock = createMockDevTools(); DevToolsShim.on("test_event", cb1); DevToolsShim.off("test_event", cb1); DevToolsShim.register(mock); checkCalls(mock, "on", 0); } function test_off_called_before_with_bad_callback() { ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); function cb1() {} function cb2() {} const mock = createMockDevTools(); DevToolsShim.on("test_event", cb1); DevToolsShim.off("test_event", cb2); DevToolsShim.register(mock); // on should still be called checkCalls(mock, "on", 1, ["test_event", cb1]); // Calls to off should not be held and forwarded. checkCalls(mock, "off", 0); } function test_events() { ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); const mock = createMockDevTools(); // Check emit was not called. checkCalls(mock, "emit", 0); // Check emit is called once with the devtools-registered event. DevToolsShim.register(mock); checkCalls(mock, "emit", 1, ["devtools-registered"]); // Check emit is called once with the devtools-unregistered event. DevToolsShim.unregister(); checkCalls(mock, "emit", 2, ["devtools-unregistered"]); } function test_restore_session_apis() { // Backup method that will be updated for the test. const initDevToolsBackup = DevToolsShim.initDevTools; // Create fake session objects to restore. const sessionWithoutDevTools = {}; const sessionWithDevTools = { browserConsole: true, }; Services.prefs.setBoolPref("devtools.policy.disabled", true); ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); ok(!DevToolsShim.isEnabled(), "DevTools are not enabled"); // Check that save & restore DevToolsSession don't initialize the tools and don't // crash. DevToolsShim.saveDevToolsSession({}); DevToolsShim.restoreDevToolsSession(sessionWithDevTools); ok(!DevToolsShim.isInitialized(), "DevTools are still not initialized"); Services.prefs.setBoolPref("devtools.policy.disabled", false); ok(DevToolsShim.isEnabled(), "DevTools are enabled"); ok(!DevToolsShim.isInitialized(), "DevTools are not initialized"); // Check that DevTools are not initialized when calling restoreDevToolsSession without // DevTools related data. DevToolsShim.restoreDevToolsSession(sessionWithoutDevTools); ok(!DevToolsShim.isInitialized(), "DevTools are still not initialized"); const mock = createMockDevTools(); DevToolsShim.initDevTools = () => { // Next call to restoreDevToolsSession is expected to initialize DevTools, which we // simulate here by registering our mock. DevToolsShim.register(mock); }; DevToolsShim.restoreDevToolsSession(sessionWithDevTools); checkCalls(mock, "restoreDevToolsSession", 1, [sessionWithDevTools]); ok(DevToolsShim.isInitialized(), "DevTools are initialized"); DevToolsShim.saveDevToolsSession({}); checkCalls(mock, "saveDevToolsSession", 1, []); // Restore initDevTools backup. DevToolsShim.initDevTools = initDevToolsBackup; } function run_test() { test_register_unregister(); DevToolsShim.unregister(); test_on_is_forwarded_to_devtools(); DevToolsShim.unregister(); test_off_called_before_registering_devtools(); DevToolsShim.unregister(); test_off_called_before_with_bad_callback(); DevToolsShim.unregister(); test_restore_session_apis(); DevToolsShim.unregister(); test_events(); }