helper-mocks.js (9949B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /* import-globals-from ../../../shared/test/shared-head.js */ 7 8 const MOCKS_ROOT = CHROME_URL_ROOT + "mocks/"; 9 /* import-globals-from mocks/helper-adb-mock.js */ 10 Services.scriptloader.loadSubScript(MOCKS_ROOT + "helper-adb-mock.js", this); 11 /* import-globals-from mocks/helper-client-wrapper-mock.js */ 12 Services.scriptloader.loadSubScript( 13 MOCKS_ROOT + "helper-client-wrapper-mock.js", 14 this 15 ); 16 /* import-globals-from mocks/helper-runtime-client-factory-mock.js */ 17 Services.scriptloader.loadSubScript( 18 MOCKS_ROOT + "helper-runtime-client-factory-mock.js", 19 this 20 ); 21 22 const { 23 RUNTIMES, 24 } = require("resource://devtools/client/aboutdebugging/src/constants.js"); 25 26 /** 27 * This wrapper around the mocks used in about:debugging tests provides helpers to 28 * quickly setup mocks for runtime tests involving USB, network or wifi runtimes that can 29 * are difficult to setup in a test environment. 30 */ 31 class Mocks { 32 constructor() { 33 // Setup the adb mock to rely on internal arrays. 34 this.adbMock = createAdbMock(); 35 this.adbProcessMock = createAdbProcessMock(); 36 this._usbRuntimes = []; 37 this._usbDevices = []; 38 this.adbMock.adb.getRuntimes = () => { 39 return this._usbRuntimes; 40 }; 41 this.adbMock.adb.getDevices = () => { 42 const runtimeDevices = this._usbRuntimes.map(r => { 43 return { id: r.deviceId, name: r.deviceName }; 44 }); 45 return runtimeDevices.concat(this._usbDevices); 46 }; 47 48 // adb.updateRuntimes should ultimately fire the "runtime-list-updated" event. 49 this.adbMock.adb.updateRuntimes = () => { 50 this.emitUSBUpdate(); 51 }; 52 53 this.adbMock.adb.isProcessStarted = () => true; 54 55 // Prepare a fake observer to be able to emit events from this mock. 56 this._observerMock = addObserverMock(this.adbMock.adb); 57 58 // Setup the runtime-client-factory mock to rely on the internal _clients map. 59 this.runtimeClientFactoryMock = createRuntimeClientFactoryMock(); 60 this._clients = { 61 [RUNTIMES.NETWORK]: {}, 62 [RUNTIMES.THIS_FIREFOX]: {}, 63 [RUNTIMES.USB]: {}, 64 }; 65 this.runtimeClientFactoryMock.createClientForRuntime = runtime => { 66 return this._clients[runtime.type][runtime.id]; 67 }; 68 69 // Add a client for THIS_FIREFOX, since about:debugging will start on the This Firefox 70 // page. 71 this._thisFirefoxClient = createThisFirefoxClientMock(); 72 this._clients[RUNTIMES.THIS_FIREFOX][RUNTIMES.THIS_FIREFOX] = 73 this._thisFirefoxClient; 74 75 // Enable mocks and remove them after the test. 76 this.enableMocks(); 77 registerCleanupFunction(() => this.disableMocks()); 78 } 79 80 get thisFirefoxClient() { 81 return this._thisFirefoxClient; 82 } 83 84 enableMocks() { 85 enableAdbMock(this.adbMock); 86 enableAdbProcessMock(this.adbProcessMock); 87 enableRuntimeClientFactoryMock(this.runtimeClientFactoryMock); 88 } 89 90 disableMocks() { 91 disableAdbMock(); 92 disableAdbProcessMock(); 93 disableRuntimeClientFactoryMock(); 94 95 for (const host of Object.keys(this._clients[RUNTIMES.NETWORK])) { 96 this.removeNetworkRuntime(host); 97 } 98 } 99 100 createNetworkRuntime(host, runtimeInfo) { 101 const { 102 addNetworkLocation, 103 } = require("resource://devtools/client/aboutdebugging/src/modules/network-locations.js"); 104 addNetworkLocation(host); 105 106 // Add a valid client that can be returned for this particular runtime id. 107 const mockNetworkClient = createClientMock(); 108 mockNetworkClient.getDeviceDescription = () => { 109 return { 110 name: runtimeInfo.name || "TestBrand", 111 channel: runtimeInfo.channel || "release", 112 version: runtimeInfo.version || "1.0", 113 }; 114 }; 115 this._clients[RUNTIMES.NETWORK][host] = mockNetworkClient; 116 117 return mockNetworkClient; 118 } 119 120 removeNetworkRuntime(host) { 121 const { 122 removeNetworkLocation, 123 } = require("resource://devtools/client/aboutdebugging/src/modules/network-locations.js"); 124 removeNetworkLocation(host); 125 126 delete this._clients[RUNTIMES.NETWORK][host]; 127 } 128 129 emitUSBUpdate() { 130 this._observerMock.emit("runtime-list-updated"); 131 } 132 133 /** 134 * Creates a USB runtime for which a client conenction can be established. 135 * 136 * @param {string} id 137 * The id of the runtime. 138 * @param {object} optional object used to create the fake runtime & device 139 * - channel: {String} Release channel, for instance "release", "nightly" 140 * - clientWrapper: {ClientWrapper} optional ClientWrapper for this runtime 141 * - deviceId: {String} Device id 142 * - deviceName: {String} Device name 143 * - isFenix: {Boolean} set by ADB if the package name matches a Fenix package 144 * - name: {String} Application name, for instance "Firefox" 145 * - shortName: {String} Short name for the device 146 * - socketPath: {String} (should only be used for connecting, so not here) 147 * - version: {String} Version, for instance "63.0a" 148 * - versionName: {String} Version return by ADB "63.0a" 149 * @return {object} Returns the mock client created for this runtime so that methods 150 * can be overridden on it. 151 */ 152 createUSBRuntime(id, runtimeInfo = {}) { 153 // Add a new runtime to the list of scanned runtimes. 154 this._usbRuntimes.push({ 155 deviceId: runtimeInfo.deviceId || "test device id", 156 deviceName: runtimeInfo.deviceName || "test device name", 157 id, 158 isFenix: runtimeInfo.isFenix, 159 shortName: runtimeInfo.shortName || "testshort", 160 socketPath: runtimeInfo.socketPath || "test/path", 161 versionName: runtimeInfo.versionName || "1.0", 162 }); 163 164 // Add a valid client that can be returned for this particular runtime id. 165 let mockUsbClient = runtimeInfo.clientWrapper; 166 if (mockUsbClient) { 167 const originalGetDeviceDescription = 168 mockUsbClient.getDeviceDescription.bind(mockUsbClient); 169 mockUsbClient.getDeviceDescription = async () => { 170 const deviceDescription = await originalGetDeviceDescription(); 171 return { 172 channel: runtimeInfo.channel || deviceDescription.channel, 173 name: runtimeInfo.name || deviceDescription.name, 174 version: runtimeInfo.version || deviceDescription.version, 175 }; 176 }; 177 } else { 178 // If no clientWrapper was provided, create a mock client here. 179 mockUsbClient = createClientMock(); 180 mockUsbClient.getDeviceDescription = () => { 181 return { 182 channel: runtimeInfo.channel || "release", 183 name: runtimeInfo.name || "TestBrand", 184 version: runtimeInfo.version || "1.0", 185 }; 186 }; 187 } 188 189 this._clients[RUNTIMES.USB][id] = mockUsbClient; 190 191 return mockUsbClient; 192 } 193 194 removeUSBRuntime(id) { 195 this._usbRuntimes = this._usbRuntimes.filter(runtime => runtime.id !== id); 196 delete this._clients[RUNTIMES.USB][id]; 197 } 198 199 addDevice(deviceId, deviceName) { 200 this._usbDevices.push({ 201 id: deviceId, 202 name: deviceName, 203 }); 204 } 205 206 removeDevice(deviceId) { 207 this._usbDevices = this._usbDevices.filter(d => { 208 return d.id !== deviceId; 209 }); 210 } 211 212 removeRuntime(id) { 213 if (this._clients[RUNTIMES.USB][id]) { 214 this.removeUSBRuntime(id); 215 } else if (this._clients[RUNTIMES.NETWORK][id]) { 216 this.removeNetworkRuntime(id); 217 } 218 } 219 } 220 /* exported Mocks */ 221 222 const silenceWorkerUpdates = function () { 223 const { 224 removeMockedModule, 225 setMockedModule, 226 } = require("resource://devtools/shared/loader/browser-loader-mocks.js"); 227 228 const mock = { 229 WorkersListener: () => { 230 return { 231 addListener: () => {}, 232 removeListener: () => {}, 233 }; 234 }, 235 }; 236 setMockedModule(mock, "devtools/client/shared/workers-listener"); 237 238 registerCleanupFunction(() => { 239 removeMockedModule("devtools/client/shared/workers-listener"); 240 }); 241 }; 242 /* exported silenceWorkerUpdates */ 243 244 async function createLocalClientWrapper() { 245 info("Create a local DevToolsClient"); 246 247 // First, instantiate a DevToolsServer, the same way it is being done when running 248 // firefox --start-debugger-server 249 const { 250 useDistinctSystemPrincipalLoader, 251 releaseDistinctSystemPrincipalLoader, 252 } = ChromeUtils.importESModule( 253 "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs" 254 ); 255 const requester = {}; 256 const serverLoader = useDistinctSystemPrincipalLoader(requester); 257 registerCleanupFunction(() => { 258 releaseDistinctSystemPrincipalLoader(requester); 259 }); 260 const { DevToolsServer } = serverLoader.require( 261 "resource://devtools/server/devtools-server.js" 262 ); 263 DevToolsServer.init(); 264 DevToolsServer.registerAllActors(); 265 DevToolsServer.allowChromeProcess = true; 266 267 // Then spawn a DevToolsClient connected to this new DevToolsServer 268 const { 269 DevToolsClient, 270 } = require("resource://devtools/client/devtools-client.js"); 271 const { 272 ClientWrapper, 273 } = require("resource://devtools/client/aboutdebugging/src/modules/client-wrapper.js"); 274 275 const client = new DevToolsClient(DevToolsServer.connectPipe()); 276 277 await client.connect(); 278 return new ClientWrapper(client); 279 } 280 /* exported createLocalClientWrapper */ 281 282 // Create a basic mock for this-firefox client, and setup a runtime-client-factory mock 283 // to return our mock client when needed. 284 function setupThisFirefoxMock() { 285 const runtimeClientFactoryMock = createRuntimeClientFactoryMock(); 286 const thisFirefoxClient = createThisFirefoxClientMock(); 287 runtimeClientFactoryMock.createClientForRuntime = runtime => { 288 if (runtime.id === RUNTIMES.THIS_FIREFOX) { 289 return thisFirefoxClient; 290 } 291 throw new Error("Unexpected runtime id " + runtime.id); 292 }; 293 294 info("Enable mocks"); 295 enableRuntimeClientFactoryMock(runtimeClientFactoryMock); 296 registerCleanupFunction(() => { 297 disableRuntimeClientFactoryMock(); 298 }); 299 300 return thisFirefoxClient; 301 } 302 /* exported setupThisFirefoxMock */