summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/browser/browser_AMBrowserExtensionsImport.js
blob: 0d3956f941ea5cef112e706e21ee8ef16177ddc4 (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

const { AMBrowserExtensionsImport } = ChromeUtils.importESModule(
  "resource://gre/modules/AddonManager.sys.mjs"
);

const { AddonTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/AddonTestUtils.sys.mjs"
);

// This test verifies the global notification in `about:addons` when there are
// pending imported add-ons. The appmenu UI is covered by tests in:
// `browser/components/extensions/test/browser/browser_AMBrowserExtensionsImport.js`.

AddonTestUtils.initMochitest(this);

const TEST_SERVER = AddonTestUtils.createHttpServer();

const ADDONS = {
  ext1: {
    manifest: {
      name: "Ext 1",
      version: "1.0",
      browser_specific_settings: { gecko: { id: "ff@ext-1" } },
      permissions: ["history"],
    },
  },
  ext2: {
    manifest: {
      name: "Ext 2",
      version: "1.0",
      browser_specific_settings: { gecko: { id: "ff@ext-2" } },
      permissions: ["history"],
    },
  },
};
// Populated in `setup()`.
const XPIS = {};
// Populated in `setup()`.
const ADDON_SEARCH_RESULTS = {};

const mockAddonRepository = ({ addons = [] }) => {
  return {
    async getMappedAddons() {
      return Promise.resolve({
        addons,
        matchedIDs: [],
        unmatchedIDs: [],
      });
    },
  };
};

const assertWarningShown = async (
  win,
  stack,
  expectedWarningType = "imported-addons",
  expectAction = true
) => {
  Assert.equal(stack.childElementCount, 1, "expected a global warning");
  const messageBar = stack.firstElementChild;
  Assert.equal(
    messageBar.getAttribute("warning-type"),
    expectedWarningType,
    `expected a warning for ${expectedWarningType}`
  );
  Assert.equal(
    messageBar.getAttribute("data-l10n-id"),
    `extensions-warning-${expectedWarningType}2`,
    "expected correct l10n ID"
  );
  await win.document.l10n.translateElements([messageBar]);

  if (expectAction) {
    const button = messageBar.querySelector("button");
    Assert.equal(
      button.getAttribute("action"),
      expectedWarningType,
      `expected a button for ${expectedWarningType}`
    );
    Assert.equal(
      button.getAttribute("data-l10n-id"),
      `extensions-warning-${expectedWarningType}-button`,
      "expected correct l10n ID on the button"
    );
    await win.document.l10n.translateElements([button]);
  }
};

add_setup(async function setup() {
  for (const [name, data] of Object.entries(ADDONS)) {
    XPIS[name] = AddonTestUtils.createTempWebExtensionFile(data);
    TEST_SERVER.registerFile(`/addons/${name}.xpi`, XPIS[name]);

    ADDON_SEARCH_RESULTS[name] = {
      id: data.manifest.browser_specific_settings.gecko.id,
      name: data.name,
      version: data.version,
      sourceURI: Services.io.newURI(
        `http://localhost:${TEST_SERVER.identity.primaryPort}/addons/${name}.xpi`
      ),
      icons: {},
    };
  }

  registerCleanupFunction(() => {
    // Clear the add-on repository override.
    AMBrowserExtensionsImport._addonRepository = null;
  });
});

add_task(async function test_aboutaddons_global_message() {
  const browserID = "some-browser-id";
  const extensionIDs = ["ext-1", "ext-2"];
  AMBrowserExtensionsImport._addonRepository = mockAddonRepository({
    addons: Object.values(ADDON_SEARCH_RESULTS),
  });

  // Global warnings should be displayed in all the `about:addons` views but
  // the migration wizard links to the default view. That's why we load this
  // view here, too (as opposed to, e.g., `"extensions"`).
  const win = await loadInitialView();
  const stack = win.document.querySelector("global-warnings");

  Assert.equal(stack.childElementCount, 0, "expected no global warning");

  let promiseTopic = TestUtils.topicObserved(
    "webextension-imported-addons-pending"
  );
  // Start a first import...
  await AMBrowserExtensionsImport.stageInstalls(browserID, extensionIDs);
  await promiseTopic;
  // We expect a warning about the imported add-ons to be shown.
  await assertWarningShown(win, stack);

  // ...then cancel it.
  promiseTopic = TestUtils.topicObserved(
    "webextension-imported-addons-cancelled"
  );
  await AMBrowserExtensionsImport.cancelInstalls();
  await promiseTopic;

  // At this point, the warning about the imported add-ons should be hidden.
  Assert.equal(stack.childElementCount, 0, "expected no global warning");

  // We start a second import here, then we make sure an imported-addons
  // messagebar doesn't prevent the other global warning types to be shown.
  promiseTopic = TestUtils.topicObserved(
    "webextension-imported-addons-pending"
  );
  const result = await AMBrowserExtensionsImport.stageInstalls(
    browserID,
    extensionIDs
  );
  await promiseTopic;
  await assertWarningShown(win, stack);

  info("Verify safe-mode is not hidden by an imported-addons messagebar");
  stack.inSafeMode = true;
  stack.refresh();
  await assertWarningShown(
    win,
    stack,
    "safe-mode",
    false /* no button expected */
  );
  stack.inSafeMode = false;

  info(
    "Verify check-compatibility is not hidden by an imported-addons messagebar"
  );
  AddonManager.checkCompatibility = false;
  stack.refresh();
  await assertWarningShown(win, stack, "check-compatibility");
  AddonManager.checkCompatibility = true;

  info("Verify update-security is not hidden by an imported-addons messagebar");
  await SpecialPowers.pushPrefEnv({
    set: [["extensions.checkUpdateSecurity", false]],
  });
  stack.refresh();
  await assertWarningShown(win, stack, "update-security");
  await SpecialPowers.popPrefEnv();

  // After making sure the imported-addons messagebar is visible again, we
  // finally complete the pending import with the UI from the global warning.
  info(
    "Verify pending imported addons can be completed from the messagebar action"
  );
  stack.refresh();
  await assertWarningShown(win, stack, "imported-addons");

  // Complete the installation of the add-ons by clicking on the button in the
  // global warning.
  promiseTopic = TestUtils.topicObserved(
    "webextension-imported-addons-complete"
  );
  const endedPromises = result.importedAddonIDs.map(id =>
    AddonTestUtils.promiseInstallEvent(
      "onInstallEnded",
      install => install.addon.id === id
    )
  );
  stack.firstElementChild.querySelector("button").click();
  await Promise.all([...endedPromises, promiseTopic]);

  // At this point, the warning about the imported add-ons should be hidden
  // because the add-ons are installed.
  Assert.equal(stack.childElementCount, 0, "expected no global warning");

  for (const id of result.importedAddonIDs) {
    const addon = await AddonManager.getAddonByID(id);
    Assert.ok(addon.isActive, `expected add-on "${id}" to be enabled`);
    await addon.uninstall();
  }

  await closeView(win);
});