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/. */
6 * This module provides the bridge between the "AppTestDelegate" helper in
7 * mochitests and the supporting implementations in AppUiTestDelegate.sys.mjs.
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:
14 * // head.js, in the scope of a plain mochitest:
15 * var { AppTestDelegate } = SpecialPowers.ChromeUtils.importESModule(
16 * "resource://specialpowers/AppTestDelegate.sys.mjs"
19 * // test usage example: open and close a tab.
20 * let tab = await AppTestDelegate.openNewForegroundTab(window, url);
21 * await AppTestDelegate.removeTab(window, tab);
23 * ## Overview of files supporting "AppTestDelegate":
25 * MOZ_BUILD_APP-specific AppUiTestDelegate.sys.mjs implementations:
26 * - browser/components/extensions/test/AppUiTestDelegate.jsm
27 * - mobile/android/modules/test/AppUiTestDelegate.jsm
28 * - mail/components/extensions/test/AppUiTestDelegate.sys.mjs (in comm-central)
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
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)
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.
41 * ## How to add new AppTestDelegate methods
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
51 * You should use the same method name across all of these files for ease of
52 * lookup and maintainability.
57 ChromeUtils.defineESModuleGetters(lazy, {
58 // Each app needs to implement this - see above comment.
59 AppUiTestDelegate: "resource://testing-common/AppUiTestDelegate.sys.mjs",
62 export class AppTestDelegateParent extends JSWindowActorParent {
65 this._tabs = new Map();
69 return this.browsingContext.top.embedderElement;
73 return this.browser.ownerGlobal;
76 async receiveMessage(message) {
77 const { extensionId, url, waitForLoad, tabId } = message.data;
78 switch (message.name) {
79 case "DOMContentLoaded":
81 return this.browser?.dispatchEvent(
82 new CustomEvent(`AppTestDelegate:${message.name}`, {
84 browsingContext: this.browsingContext,
90 case "clickPageAction":
91 return lazy.AppUiTestDelegate.clickPageAction(this.window, extensionId);
92 case "clickBrowserAction":
93 return lazy.AppUiTestDelegate.clickBrowserAction(
97 case "closePageAction":
98 return lazy.AppUiTestDelegate.closePageAction(this.window, extensionId);
99 case "closeBrowserAction":
100 return lazy.AppUiTestDelegate.closeBrowserAction(
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(
113 case "openNewForegroundTab": {
114 // We cannot send the tab object across process so let's store it with
116 const uuid = Services.uuid.generateUUID().toString();
117 const tab = await lazy.AppUiTestDelegate.openNewForegroundTab(
122 this._tabs.set(uuid, tab);
126 const tab = this._tabs.get(tabId);
127 this._tabs.delete(tabId);
128 return lazy.AppUiTestDelegate.removeTab(tab);
132 throw new Error(`Unknown Test API: ${message.name}.`);