summaryrefslogtreecommitdiffstats
path: root/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_addons_debug_console.js
blob: 1612cd545f84938c480d765755e1d534690b9a7c (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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

/* import-globals-from helper-addons.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-addons.js", this);

// There are shutdown issues for which multiple rejections are left uncaught.
// See bug 1018184 for resolving these issues.
const { PromiseTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/PromiseTestUtils.sys.mjs"
);
PromiseTestUtils.allowMatchingRejectionsGlobally(/File closed/);

// 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 = "base-test-devtools-webextension";

const OTHER_ADDON_ID = "other-test-devtools-webextension@mozilla.org";
const OTHER_ADDON_NAME = "other-test-devtools-webextension";

const POPUPONLY_ADDON_ID = "popuponly-test-devtools-webextension@mozilla.org";
const POPUPONLY_ADDON_NAME = "popuponly-test-devtools-webextension";

const BACKGROUND_ADDON_ID = "background-test-devtools-webextension@mozilla.org";
const BACKGROUND_ADDON_NAME = "background-test-devtools-webextension";

/**
 * 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;
 */
add_task(async function testWebExtensionsToolboxWebConsole() {
  await pushPref("devtools.webconsole.filter.css", true);
  await enableExtensionDebugging();
  const { document, tab, window } = await openAboutDebugging();
  await selectThisFirefoxPage(document, window.AboutDebugging.store);

  await installTemporaryExtensionFromXPI(
    {
      background() {
        window.myWebExtensionAddonFunction = function () {
          console.log(
            "Background page function called",
            this.browser.runtime.getManifest()
          );
        };

        const style = document.createElement("style");
        style.textContent = "* { color: error; }";
        document.documentElement.appendChild(style);

        throw new Error("Background page exception");
      },
      extraProperties: {
        browser_action: {
          default_title: "WebExtension Popup Debugging",
          default_popup: "popup.html",
          default_area: "navbar",
        },
      },
      files: {
        "popup.html": `<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <script src="popup.js"></script>
          </head>
          <body>
            Popup
          </body>
        </html>
      `,
        "popup.js": function () {
          console.log("Popup log");

          const style = document.createElement("style");
          style.textContent = "* { color: popup-error; }";
          document.documentElement.appendChild(style);

          throw new Error("Popup exception");
        },
      },
      id: ADDON_ID,
      name: ADDON_NAME,
    },
    document
  );

  // Install another addon in order to ensure we don't get its logs
  await installTemporaryExtensionFromXPI(
    {
      background() {
        console.log("Other addon log");

        const style = document.createElement("style");
        style.textContent = "* { background-color: error; }";
        document.documentElement.appendChild(style);

        throw new Error("Other addon exception");
      },
      extraProperties: {
        browser_action: {
          default_title: "Other addon popup",
          default_popup: "other-popup.html",
          default_area: "navbar",
        },
      },
      files: {
        "other-popup.html": `<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <script src="other-popup.js"></script>
          </head>
          <body>
            Other popup
          </body>
        </html>
      `,
        "other-popup.js": function () {
          console.log("Other popup log");

          const style = document.createElement("style");
          style.textContent = "* { background-color: popup-error; }";
          document.documentElement.appendChild(style);

          throw new Error("Other popup exception");
        },
      },
      id: OTHER_ADDON_ID,
      name: OTHER_ADDON_NAME,
    },
    document
  );

  const { devtoolsWindow } = await openAboutDevtoolsToolbox(
    document,
    tab,
    window,
    ADDON_NAME
  );
  const toolbox = getToolbox(devtoolsWindow);
  const webconsole = await toolbox.selectTool("webconsole");
  const { hud } = webconsole;

  info("Trigger some code in the background page logging some stuff");
  const onMessage = waitUntil(() => {
    return !!findMessagesByType(hud, "Background page exception", ".error")
      .length;
  });
  hud.ui.wrapper.dispatchEvaluateExpression("myWebExtensionAddonFunction()");
  await onMessage;

  info("Open the two add-ons popups to cover popups messages");
  const onPopupMessage = waitUntil(() => {
    return !!findMessagesByType(hud, "Popup exception", ".error").length;
  });
  clickOnAddonWidget(OTHER_ADDON_ID);
  clickOnAddonWidget(ADDON_ID);
  await onPopupMessage;

  info("Wait a bit to catch unexpected duplicates or mixed up messages");
  await wait(1000);

  is(
    findMessagesByType(hud, "Background page exception", ".error").length,
    1,
    "We get the background page exception"
  );
  is(
    findMessagesByType(hud, "Popup exception", ".error").length,
    1,
    "We get the popup exception"
  );
  is(
    findMessagesByType(
      hud,
      "Expected color but found ‘error’.  Error in parsing value for ‘color’.  Declaration dropped.",
      ".warn"
    ).length,
    1,
    "We get the addon's background page CSS error message"
  );
  is(
    findMessagesByType(
      hud,
      "Expected color but found ‘popup-error’.  Error in parsing value for ‘color’.  Declaration dropped.",
      ".warn"
    ).length,
    1,
    "We get the addon's popup CSS error message"
  );

  // Verify that we don't get the other addon log and errors
  ok(
    !findMessageByType(hud, "Other addon log", ".console-api"),
    "We don't get the other addon log"
  );
  ok(
    !findMessageByType(hud, "Other addon exception", ".console-api"),
    "We don't get the other addon exception"
  );
  ok(
    !findMessageByType(hud, "Other popup log", ".console-api"),
    "We don't get the other addon popup log"
  );
  ok(
    !findMessageByType(hud, "Other popup exception", ".error"),
    "We don't get the other addon popup exception"
  );
  ok(
    !findMessageByType(
      hud,
      "Expected color but found ‘error’.  Error in parsing value for ‘background-color’.  Declaration dropped.",
      ".warn"
    ),
    "We don't get the other addon's background page CSS error message"
  );
  ok(
    !findMessageByType(
      hud,
      "Expected color but found ‘popup-error’.  Error in parsing value for ‘background-color’.  Declaration dropped.",
      ".warn"
    ),
    "We don't get the other addon's popup CSS error message"
  );

  // Verify that console evaluations still work after reloading the page
  info("Reload the webextension document");
  const { onDomCompleteResource } =
    await waitForNextTopLevelDomCompleteResource(toolbox.commands);
  hud.ui.wrapper.dispatchEvaluateExpression("location.reload()");
  await onDomCompleteResource;

  info("Try to evaluate something after reload");

  const onEvaluationResultAfterReload = waitUntil(() =>
    findMessageByType(hud, "result:2", ".result")
  );
  const onMessageAfterReload = waitUntil(() =>
    findMessageByType(hud, "message after reload", ".console-api")
  );
  hud.ui.wrapper.dispatchEvaluateExpression(
    "console.log('message after reload'); 'result:' + (1 + 1)"
  );
  // Both cover that the console.log worked
  await onMessageAfterReload;
  // And we received the evaluation result
  await onEvaluationResultAfterReload;

  await closeWebExtAboutDevtoolsToolbox(devtoolsWindow, window);

  // Note that it seems to be important to remove the addons in the reverse order
  // from which they were installed...
  await removeTemporaryExtension(OTHER_ADDON_NAME, document);
  await removeTemporaryExtension(ADDON_NAME, document);
  await removeTab(tab);
});

add_task(async function testWebExtensionNoBgScript() {
  await pushPref("devtools.webconsole.filter.css", true);
  await enableExtensionDebugging();
  const { document, tab, window } = await openAboutDebugging();
  await selectThisFirefoxPage(document, window.AboutDebugging.store);

  await installTemporaryExtensionFromXPI(
    {
      extraProperties: {
        browser_action: {
          default_title: "WebExtension Popup Only",
          default_popup: "popup.html",
          default_area: "navbar",
        },
      },
      files: {
        "popup.html": `<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <script src="popup.js"></script>
          </head>
          <body>
            Popup
          </body>
        </html>
      `,
        "popup.js": function () {
          console.log("Popup-only log");

          const style = document.createElement("style");
          style.textContent = "* { color: popup-only-error; }";
          document.documentElement.appendChild(style);

          throw new Error("Popup-only exception");
        },
      },
      id: POPUPONLY_ADDON_ID,
      name: POPUPONLY_ADDON_NAME,
    },
    document
  );

  const { devtoolsWindow } = await openAboutDevtoolsToolbox(
    document,
    tab,
    window,
    POPUPONLY_ADDON_NAME
  );
  const toolbox = getToolbox(devtoolsWindow);
  const webconsole = await toolbox.selectTool("webconsole");
  const { hud } = webconsole;

  info("Open the add-on popup");
  const onPopupMessage = waitUntil(() => {
    return !!findMessagesByType(hud, "Popup-only exception", ".error").length;
  });
  clickOnAddonWidget(POPUPONLY_ADDON_ID);
  await onPopupMessage;

  info("Wait a bit to catch unexpected duplicates or mixed up messages");
  await wait(1000);
  is(
    findMessagesByType(hud, "Popup-only exception", ".error").length,
    1,
    "We get the popup exception"
  );
  is(
    findMessagesByType(hud, "Popup-only log", ".console-api").length,
    1,
    "We get the addon's popup log"
  );
  is(
    findMessagesByType(
      hud,
      "Expected color but found ‘popup-only-error’.  Error in parsing value for ‘color’.  Declaration dropped.",
      ".warn"
    ).length,
    1,
    "We get the addon's popup CSS error message"
  );

  await closeWebExtAboutDevtoolsToolbox(devtoolsWindow, window);
  await removeTemporaryExtension(POPUPONLY_ADDON_NAME, document);
  await removeTab(tab);
});

// Check that reloading the addon several times does not break the console,
// see Bug 1778951.
add_task(async function testWebExtensionTwoReloads() {
  await enableExtensionDebugging();
  const { document, tab, window } = await openAboutDebugging();
  await selectThisFirefoxPage(document, window.AboutDebugging.store);

  await installTemporaryExtensionFromXPI(
    {
      background() {
        console.log("Background page log");
      },
      extraProperties: {
        browser_action: {
          default_title: "WebExtension with background script",
          default_popup: "popup.html",
          default_area: "navbar",
        },
      },
      files: {
        "popup.html": `<!DOCTYPE html>
        <html>
          <body>
            Popup
          </body>
        </html>
      `,
      },
      id: BACKGROUND_ADDON_ID,
      name: BACKGROUND_ADDON_NAME,
    },
    document
  );

  // Retrieve the addonTarget element before calling `openAboutDevtoolsToolbox`,
  // otherwise it will pick the about:devtools-toolbox tab with the same name
  // instead.
  const addonTarget = findDebugTargetByText(BACKGROUND_ADDON_NAME, document);

  const { devtoolsWindow } = await openAboutDevtoolsToolbox(
    document,
    tab,
    window,
    BACKGROUND_ADDON_NAME
  );
  const toolbox = getToolbox(devtoolsWindow);
  const webconsole = await toolbox.selectTool("webconsole");
  const { hud } = webconsole;

  // Verify that console evaluations still work after reloading the addon
  info("Reload the webextension itself");
  let { onDomCompleteResource } = await waitForNextTopLevelDomCompleteResource(
    toolbox.commands
  );
  const reloadButton = addonTarget.querySelector(
    ".qa-temporary-extension-reload-button"
  );
  reloadButton.click();
  await onDomCompleteResource;

  info("Try to evaluate something after 1st addon reload");
  // Wait before evaluating the message, otherwise they might be cleaned up by
  // the console UI.
  info("Wait until the background script log is visible");
  await waitUntil(() =>
    findMessageByType(hud, "Background page log", ".message")
  );

  hud.ui.wrapper.dispatchEvaluateExpression("40+1");
  await waitUntil(() => findMessageByType(hud, "41", ".result"));

  info("Reload the extension a second time");
  ({ onDomCompleteResource } = await waitForNextTopLevelDomCompleteResource(
    toolbox.commands
  ));
  reloadButton.click();
  await onDomCompleteResource;

  info("Wait until the background script log is visible - after reload");
  await waitUntil(() =>
    findMessageByType(hud, "Background page log", ".message")
  );

  info("Try to evaluate something after 2nd addon reload");
  hud.ui.wrapper.dispatchEvaluateExpression("40+2");
  await waitUntil(() => findMessageByType(hud, "42", ".result"));

  await closeWebExtAboutDevtoolsToolbox(devtoolsWindow, window);
  await removeTemporaryExtension(BACKGROUND_ADDON_NAME, document);
  await removeTab(tab);
});