AppTestDelegateParent.sys.mjs (4976B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * This module provides the bridge between the "AppTestDelegate" helper in 7 * mochitests and the supporting implementations in AppUiTestDelegate.sys.mjs. 8 * 9 * "AppTestDelegate" is documented in AppTestDelegate.sys.mjs and enables 10 * mochitests to invoke common functionality whose implementation is different 11 * (e.g. in browser/ and mobile/ instead of toolkit/). 12 * Tests can use this common interface after importing AppTestDelegate.sys.mjs: 13 * 14 * // head.js, in the scope of a plain mochitest: 15 * var { AppTestDelegate } = SpecialPowers.ChromeUtils.importESModule( 16 * "resource://specialpowers/AppTestDelegate.sys.mjs" 17 * ); 18 * 19 * // test usage example: open and close a tab. 20 * let tab = await AppTestDelegate.openNewForegroundTab(window, url); 21 * await AppTestDelegate.removeTab(window, tab); 22 * 23 * ## Overview of files supporting "AppTestDelegate": 24 * 25 * MOZ_BUILD_APP-specific AppUiTestDelegate.sys.mjs implementations: 26 * - browser/components/extensions/test/AppUiTestDelegate.sys.mjs 27 * - mobile/shared/modules/test/AppUiTestDelegate.sys.mjs 28 * - mail/components/extensions/test/AppUiTestDelegate.sys.mjs (in comm-central) 29 * 30 * Glue between AppUiTestDelegate.sys.mjs in parent and test code in child: 31 * - testing/specialpowers/content/AppTestDelegateParent.sys.mjs (this file) 32 * - testing/specialpowers/content/AppTestDelegateChild.sys.mjs 33 * - testing/specialpowers/content/AppTestDelegate.sys.mjs 34 * 35 * Setup for usage by test code in child (i.e. plain mochitests): 36 * - Import AppTestDelegate.sys.mjs (e.g. in head.js or the test) 37 * 38 * Note: some browser-chrome tests import AppUiTestDelegate.sys.mjs directly, 39 * but that is not part of this API contract. They merely reuse code. 40 * 41 * ## How to add new AppTestDelegate methods 42 * 43 * - Add the method to AppTestDelegate.sys.mjs 44 * - Add a message forwarder in AppTestDelegateChild.sys.mjs 45 * - Add a message handler in AppTestDelegateParent.sys.mjs 46 * - Add an implementation in AppUiTestDelegate.sys.mjs for each MOZ_BUILD_APP, 47 * by defining the method on the exported AppUiTestDelegate object. 48 * All AppUiTestDelegate implementations must be kept in sync to have the 49 * same interface! 50 * 51 * You should use the same method name across all of these files for ease of 52 * lookup and maintainability. 53 */ 54 55 const lazy = {}; 56 57 ChromeUtils.defineESModuleGetters(lazy, { 58 // Each app needs to implement this - see above comment. 59 AppUiTestDelegate: "resource://testing-common/AppUiTestDelegate.sys.mjs", 60 }); 61 62 export class AppTestDelegateParent extends JSWindowActorParent { 63 constructor() { 64 super(); 65 this._tabs = new Map(); 66 } 67 68 get browser() { 69 return this.browsingContext.top.embedderElement; 70 } 71 72 get window() { 73 return this.browser.ownerGlobal; 74 } 75 76 async receiveMessage(message) { 77 const { extensionId, url, waitForLoad, tabId } = message.data; 78 switch (message.name) { 79 case "DOMContentLoaded": 80 case "load": { 81 return this.browser?.dispatchEvent( 82 new CustomEvent(`AppTestDelegate:${message.name}`, { 83 detail: { 84 browsingContext: this.browsingContext, 85 ...message.data, 86 }, 87 }) 88 ); 89 } 90 case "clickPageAction": 91 return lazy.AppUiTestDelegate.clickPageAction(this.window, extensionId); 92 case "clickBrowserAction": 93 return lazy.AppUiTestDelegate.clickBrowserAction( 94 this.window, 95 extensionId 96 ); 97 case "closePageAction": 98 return lazy.AppUiTestDelegate.closePageAction(this.window, extensionId); 99 case "closeBrowserAction": 100 return lazy.AppUiTestDelegate.closeBrowserAction( 101 this.window, 102 extensionId 103 ); 104 case "awaitExtensionPanel": 105 // The desktop delegate returns a <browser>, but that cannot be sent 106 // over IPC, so just ignore it. The promise resolves when the panel and 107 // its content is fully loaded. 108 await lazy.AppUiTestDelegate.awaitExtensionPanel( 109 this.window, 110 extensionId 111 ); 112 return null; 113 case "openNewForegroundTab": { 114 // We cannot send the tab object across process so let's store it with 115 // a unique ID here. 116 const uuid = Services.uuid.generateUUID().toString(); 117 const tab = await lazy.AppUiTestDelegate.openNewForegroundTab( 118 this.window, 119 url, 120 waitForLoad 121 ); 122 this._tabs.set(uuid, tab); 123 return uuid; 124 } 125 case "removeTab": { 126 const tab = this._tabs.get(tabId); 127 this._tabs.delete(tabId); 128 return lazy.AppUiTestDelegate.removeTab(tab); 129 } 130 131 default: 132 throw new Error(`Unknown Test API: ${message.name}.`); 133 } 134 } 135 }