summaryrefslogtreecommitdiffstats
path: root/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
blob: d2cb8031ebc867238dec2b2eca51cbb7dfc82cca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

// Avoid test timeouts that can occur while waiting for the "addon-console-works" message.
requestLongerTimeout(2);

const ADDON_ID = "test-devtools-webextension@mozilla.org";
const ADDON_NAME = "test-devtools-webextension";
const ADDON_MANIFEST_PATH = "addons/test-devtools-webextension/manifest.json";

const {
  BrowserToolboxProcess
} = Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});

/**
 * This test file ensures that the webextension addon developer toolbox:
 * - when the debug button is clicked on a webextension, the opened toolbox
 *   has a working webconsole with the background page as default target;
 * - the webextension developer toolbox has a working Inspector panel, with the
 *   background page as default target;
 * - the webextension developer toolbox is connected to a fallback page when the
 *   background page is not available (and in the fallback page document body contains
 *   the expected message, which warns the user that the current page is not a real
 *   webextension context);
 * - the webextension developer toolbox has a frame list menu and the noautohide toolbar
 *   toggle button, and they can be used to switch the current target to the extension
 *   popup page.
 */

/**
 * Returns the widget id for an extension with the passed id.
 */
function makeWidgetId(id) {
  id = id.toLowerCase();
  return id.replace(/[^a-z0-9_-]/g, "_");
}

add_task(function* testWebExtensionsToolboxSwitchToPopup() {
  let {
    tab, document, debugBtn,
  } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);

  let onReadyForOpenPopup = new Promise(done => {
    Services.obs.addObserver(function listener(message, topic) {
      let apiMessage = message.wrappedJSObject;
      if (!apiMessage.originAttributes ||
          apiMessage.originAttributes.addonId != ADDON_ID) {
        return;
      }

      if (apiMessage.arguments[0] == "readyForOpenPopup") {
        Services.obs.removeObserver(listener, "console-api-log-event");
        done();
      }
    }, "console-api-log-event", false);
  });

  // Be careful, this JS function is going to be executed in the addon toolbox,
  // which lives in another process. So do not try to use any scope variable!
  let env = Cc["@mozilla.org/process/environment;1"]
        .getService(Ci.nsIEnvironment);
  let testScript = function () {
    /* eslint-disable no-undef */

    let jsterm;
    let popupFramePromise;

    toolbox.selectTool("webconsole")
      .then(console => {
        dump(`Clicking the noautohide button\n`);
        toolbox.doc.getElementById("command-button-noautohide").click();
        dump(`Clicked the noautohide button\n`);

        popupFramePromise = new Promise(resolve => {
          let listener = (event, data) => {
            if (data.frames.some(({url}) => url && url.endsWith("popup.html"))) {
              toolbox.target.off("frame-update", listener);
              resolve();
            }
          };
          toolbox.target.on("frame-update", listener);
        });

        let waitForFrameListUpdate = new Promise((done) => {
          toolbox.target.once("frame-update", () => {
            done(console);
          });
        });

        jsterm = console.hud.jsterm;
        jsterm.execute("myWebExtensionShowPopup()");

        // Wait the initial frame update (which list the background page).
        return waitForFrameListUpdate;
      })
      .then((console) => {
        // Wait the new frame update (once the extension popup has been opened).
        return popupFramePromise;
      })
      .then(() => {
        dump(`Clicking the frame list button\n`);
        let btn = toolbox.doc.getElementById("command-button-frames");
        let menu = toolbox.showFramesMenu({target: btn});
        dump(`Clicked the frame list button\n`);
        return menu.once("open").then(() => {
          return menu;
        });
      })
      .then(frameMenu => {
        let frames = frameMenu.items;

        if (frames.length != 2) {
          throw Error(`Number of frames found is wrong: ${frames.length} != 2`);
        }

        let popupFrameBtn = frames.filter((frame) => {
          return frame.label.endsWith("popup.html");
        }).pop();

        if (!popupFrameBtn) {
          throw Error("Extension Popup frame not found in the listed frames");
        }

        let waitForNavigated = toolbox.target.once("navigate");

        popupFrameBtn.click();

        return waitForNavigated;
      })
      .then(() => {
        return jsterm.execute("myWebExtensionPopupAddonFunction()");
      })
      .then(() => toolbox.destroy())
      .catch((error) => {
        dump("Error while running code in the browser toolbox process:\n");
        dump(error + "\n");
        dump("stack:\n" + error.stack + "\n");
      });
    /* eslint-enable no-undef */
  };
  env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
  registerCleanupFunction(() => {
    env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
  });

  // Wait for a notification sent by a script evaluated the test addon via
  // the web console.
  let onPopupCustomMessage = new Promise(done => {
    Services.obs.addObserver(function listener(message, topic) {
      let apiMessage = message.wrappedJSObject;
      if (!apiMessage.originAttributes ||
          apiMessage.originAttributes.addonId != ADDON_ID) {
        return;
      }

      if (apiMessage.arguments[0] == "Popup page function called") {
        Services.obs.removeObserver(listener, "console-api-log-event");
        done(apiMessage.arguments);
      }
    }, "console-api-log-event", false);
  });

  let onToolboxClose = BrowserToolboxProcess.once("close");

  debugBtn.click();

  yield onReadyForOpenPopup;

  let browserActionId = makeWidgetId(ADDON_ID) + "-browser-action";
  let browserActionEl = window.document.getElementById(browserActionId);

  ok(browserActionEl, "Got the browserAction button from the browser UI");
  browserActionEl.click();
  info("Clicked on the browserAction button");

  let args = yield onPopupCustomMessage;
  ok(true, "Received console message from the popup page function as expected");
  is(args[0], "Popup page function called", "Got the expected console message");
  is(args[1] && args[1].name, ADDON_NAME,
     "Got the expected manifest from WebExtension API");

  yield onToolboxClose;

  ok(true, "Addon toolbox closed");

  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
  yield closeAboutDebugging(tab);
});