Backed out changeset 555c786396f8 (bug 1852046) as requested. CLOSED TREE
[gecko.git] / toolkit / mozapps / extensions / test / xpcshell / test_AddonRepository.js
blob6f1c99eaa801085dedc276d63e880035079d6b2e
1 /* Any copyright is dedicated to the Public Domain.
2  * http://creativecommons.org/publicdomain/zero/1.0/
3  */
5 // Tests AddonRepository.jsm
7 var gServer = createHttpServer({ hosts: ["example.com"] });
9 const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
10 const PREF_GETADDONS_BROWSESEARCHRESULTS =
11   "extensions.getAddons.search.browseURL";
12 const PREF_GET_BROWSER_MAPPINGS = "extensions.getAddons.browserMappings.url";
14 const BASE_URL = "http://example.com";
15 const DEFAULT_URL = "about:blank";
17 const ADDONS = [
18   {
19     manifest: {
20       name: "XPI Add-on 1",
21       version: "1.1",
22       browser_specific_settings: {
23         gecko: { id: "test_AddonRepository_1@tests.mozilla.org" },
24       },
25     },
26   },
27   {
28     manifest: {
29       name: "XPI Add-on 2",
30       version: "1.2",
31       theme: {},
32       browser_specific_settings: {
33         gecko: { id: "test_AddonRepository_2@tests.mozilla.org" },
34       },
35     },
36   },
37   {
38     manifest: {
39       name: "XPI Add-on 3",
40       version: "1.3",
41       theme: {},
42       browser_specific_settings: {
43         gecko: { id: "test_AddonRepository_3@tests.mozilla.org" },
44       },
45     },
46   },
49 // Path to source URI of installing add-on
50 const INSTALL_URL2 = "/addons/test_AddonRepository_2.xpi";
51 // Path to source URI of non-active add-on (state = STATE_AVAILABLE)
52 const INSTALL_URL3 = "/addons/test_AddonRepository_3.xpi";
54 // Properties of an individual add-on that should be checked
55 // Note: name is checked separately
56 var ADDON_PROPERTIES = [
57   "id",
58   "type",
59   "version",
60   "creator",
61   "developers",
62   "description",
63   "fullDescription",
64   "iconURL",
65   "icons",
66   "screenshots",
67   "supportURL",
68   "contributionURL",
69   "averageRating",
70   "reviewCount",
71   "reviewURL",
72   "weeklyDownloads",
73   "dailyUsers",
74   "sourceURI",
75   "updateDate",
76   "amoListingURL",
79 // Results of getAddonsByIDs
80 var GET_RESULTS = [
81   {
82     id: "test1@tests.mozilla.org",
83     type: "extension",
84     version: "1.1",
85     creator: {
86       name: "Test Creator 1",
87       url: BASE_URL + "/creator1.html",
88     },
89     developers: [
90       {
91         name: "Test Developer 1",
92         url: BASE_URL + "/developer1.html",
93       },
94     ],
95     description: "Test Summary 1",
96     fullDescription: "Test Description 1",
97     iconURL: BASE_URL + "/icon1.png",
98     icons: { 32: BASE_URL + "/icon1.png" },
99     screenshots: [
100       {
101         url: BASE_URL + "/full1-1.png",
102         width: 400,
103         height: 300,
104         thumbnailURL: BASE_URL + "/thumbnail1-1.png",
105         thumbnailWidth: 200,
106         thumbnailHeight: 150,
107         caption: "Caption 1 - 1",
108       },
109       {
110         url: BASE_URL + "/full2-1.png",
111         thumbnailURL: BASE_URL + "/thumbnail2-1.png",
112         caption: "Caption 2 - 1",
113       },
114     ],
115     supportURL: BASE_URL + "/support1.html",
116     contributionURL: BASE_URL + "/contribution1.html",
117     averageRating: 4,
118     reviewCount: 1111,
119     reviewURL: BASE_URL + "/review1.html",
120     weeklyDownloads: 3333,
121     sourceURI: BASE_URL + INSTALL_URL2,
122     updateDate: new Date(1265033045000),
123     amoListingURL:
124       "https://addons.mozilla.org/en-US/firefox/addon/test1@tests.mozilla.org/",
125   },
126   {
127     id: "test2@tests.mozilla.org",
128     type: "extension",
129     version: "2.0",
130     icons: {},
131     sourceURI: "http://example.com/addons/bleah.xpi",
132   },
133   {
134     id: "test_AddonRepository_1@tests.mozilla.org",
135     type: "theme",
136     version: "1.4",
137     icons: {},
138   },
141 // Values for testing AddonRepository.getAddonsByIDs()
142 var GET_TEST = {
143   preference: PREF_GETADDONS_BYIDS,
144   preferenceValue: BASE_URL + "/%OS%/%VERSION%/%IDS%",
145   failedIDs: ["test1@tests.mozilla.org"],
146   failedURL: "/XPCShell/1/test1%40tests.mozilla.org",
147   successfulIDs: [
148     "test1@tests.mozilla.org",
149     "test2@tests.mozilla.org",
150     "{00000000-1111-2222-3333-444444444444}",
151     "test_AddonRepository_1@tests.mozilla.org",
152   ],
153   successfulURL:
154     "/XPCShell/1/test1%40tests.mozilla.org%2C" +
155     "test2%40tests.mozilla.org%2C" +
156     "%7B00000000-1111-2222-3333-444444444444%7D%2C" +
157     "test_AddonRepository_1%40tests.mozilla.org",
158   successfulRTAURL:
159     "/XPCShell/1/rta%3AdGVzdDFAdGVzdHMubW96aWxsYS5vcmc%2C" +
160     "test2%40tests.mozilla.org%2C" +
161     "%7B00000000-1111-2222-3333-444444444444%7D%2C" +
162     "test_AddonRepository_1%40tests.mozilla.org",
165 const GET_BROWSER_MAPPINGS_URL = `${BASE_URL}/browser-mappings/%BROWSER%`;
167 // Test that actual results and expected results are equal
168 function check_results(aActualAddons, aExpectedAddons) {
169   do_check_addons(aActualAddons, aExpectedAddons, ADDON_PROPERTIES);
171   // Additional tests
172   aActualAddons.forEach(function check_each_addon(aActualAddon) {
173     // Separately check name so better messages are output when test fails
174     if (aActualAddon.name == "FAIL") {
175       do_throw(aActualAddon.id + " - " + aActualAddon.description);
176     }
177     if (aActualAddon.name != "PASS") {
178       do_throw(aActualAddon.id + " - invalid add-on name " + aActualAddon.name);
179     }
180   });
183 add_task(async function setup() {
184   // Setup for test
185   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
187   let xpis = ADDONS.map(addon => createTempWebExtensionFile(addon));
189   // Register other add-on XPI files
190   gServer.registerFile(INSTALL_URL2, xpis[1]);
191   gServer.registerFile(INSTALL_URL3, xpis[2]);
193   // Register files used to test search failure
194   gServer.registerFile(
195     GET_TEST.failedURL,
196     do_get_file("data/test_AddonRepository_fail.json")
197   );
199   // Register files used to test search success
200   gServer.registerFile(
201     GET_TEST.successfulURL,
202     do_get_file("data/test_AddonRepository_getAddonsByIDs.json")
203   );
204   // Register file for RTA test
205   gServer.registerFile(
206     GET_TEST.successfulRTAURL,
207     do_get_file("data/test_AddonRepository_getAddonsByIDs.json")
208   );
210   // Register some files/handlers for browser mapping tests.
211   gServer.registerFile(
212     // Keep in sync with `GET_BROWSER_MAPPINGS_URL`.
213     "/browser-mappings/valid-browser-id",
214     do_get_file("data/test_AddonRepository_getMappedAddons.json")
215   );
216   gServer.registerFile(
217     // This is used in `test_getMappedAddons_empty_mapping()` and should be
218     // updated if `GET_BROWSER_MAPPINGS_URL` is also updated.
219     "/browser-mappings/browser-id-empty-results",
220     do_get_file("data/test_AddonRepository_getMappedAddons_empty.json")
221   );
222   gServer.registerPrefixHandler(
223     // Keep in sync with the pref set in `test_getMappedAddons_with_paging()`.
224     "/browser-mappings/with-paging/valid-browser-id/",
225     // This handler parses the query string of the request it receives in order
226     // to force the `getMappedAddons()` method to call the same API endpoint a
227     // few times (by incrementing the integer value in the query string every
228     // time). After that, this handler returns a "next' URL that points to one
229     // of the valid endpoints registered above, which won't have a "next" URL.
230     // We do this to verify that `getMappedAddons()` supports paginated API
231     // results.
232     (request, response) => {
233       const page = parseInt(request.queryString, 10);
234       const nextPath =
235         page < 3
236           ? `with-paging/valid-browser-id/?${page + 1}`
237           : `valid-browser-id`;
239       response.setHeader("content-type", "application/json");
240       response.write(
241         JSON.stringify({
242           count: 0,
243           next: `${BASE_URL}/browser-mappings/${nextPath}`,
244           page_count: page,
245           page_size: 1,
246           previous: null,
247           results: [],
248         })
249       );
250     }
251   );
253   await promiseStartupManager();
255   // Install an add-on so can check that it isn't returned in the results
256   await promiseInstallFile(xpis[0]);
257   await promiseRestartManager();
259   // Create an active AddonInstall so can check that it isn't returned in the results
260   let install = await AddonManager.getInstallForURL(BASE_URL + INSTALL_URL2);
261   let promise = promiseCompleteInstall(install);
262   registerCleanupFunction(() => promise);
264   // Create a non-active AddonInstall so can check that it is returned in the results
265   await AddonManager.getInstallForURL(BASE_URL + INSTALL_URL3);
268 // Tests homepageURL and getSearchURL()
269 add_task(async function test_1() {
270   function check_urls(aPreference, aGetURL, aTests) {
271     aTests.forEach(function (aTest) {
272       Services.prefs.setCharPref(aPreference, aTest.preferenceValue);
273       Assert.equal(aGetURL(aTest), aTest.expectedURL);
274     });
275   }
277   var urlTests = [
278     {
279       preferenceValue: BASE_URL,
280       expectedURL: BASE_URL,
281     },
282     {
283       preferenceValue: BASE_URL + "/%OS%/%VERSION%",
284       expectedURL: BASE_URL + "/XPCShell/1",
285     },
286   ];
288   // Extra tests for AddonRepository.getSearchURL();
289   var searchURLTests = [
290     {
291       searchTerms: "test",
292       preferenceValue: BASE_URL + "/search?q=%TERMS%",
293       expectedURL: BASE_URL + "/search?q=test",
294     },
295     {
296       searchTerms: "test search",
297       preferenceValue: BASE_URL + "/%TERMS%",
298       expectedURL: BASE_URL + "/test%20search",
299     },
300     {
301       searchTerms: 'odd=search:with&weird"characters',
302       preferenceValue: BASE_URL + "/%TERMS%",
303       expectedURL: BASE_URL + "/odd%3Dsearch%3Awith%26weird%22characters",
304     },
305   ];
307   // Setup tests for homepageURL and getSearchURL()
308   var tests = [
309     {
310       initiallyUndefined: true,
311       preference: PREF_GETADDONS_BROWSEADDONS,
312       urlTests,
313       getURL: () => AddonRepository.homepageURL,
314     },
315     {
316       initiallyUndefined: false,
317       preference: PREF_GETADDONS_BROWSESEARCHRESULTS,
318       urlTests: urlTests.concat(searchURLTests),
319       getURL: function getSearchURL(aTest) {
320         var searchTerms =
321           aTest && aTest.searchTerms ? aTest.searchTerms : "unused terms";
322         return AddonRepository.getSearchURL(searchTerms);
323       },
324     },
325   ];
327   tests.forEach(function url_test(aTest) {
328     if (aTest.initiallyUndefined) {
329       // Preference is not defined by default
330       Assert.equal(
331         Services.prefs.getPrefType(aTest.preference),
332         Services.prefs.PREF_INVALID
333       );
334       Assert.equal(aTest.getURL(), DEFAULT_URL);
335     }
337     check_urls(aTest.preference, aTest.getURL, aTest.urlTests);
338   });
341 // Tests failure of AddonRepository.getAddonsByIDs()
342 add_task(async function test_getAddonsByID_fails() {
343   Services.prefs.setCharPref(GET_TEST.preference, GET_TEST.preferenceValue);
345   await Assert.rejects(
346     AddonRepository.getAddonsByIDs(GET_TEST.failedIDs),
347     /Error: GET.*?failed/
348   );
351 // Tests success of AddonRepository.getAddonsByIDs()
352 add_task(async function test_getAddonsByID_succeeds() {
353   let result = await AddonRepository.getAddonsByIDs(GET_TEST.successfulIDs);
355   check_results(result, GET_RESULTS);
358 // Tests success of AddonRepository.getAddonsByIDs() with rta ID.
359 add_task(async function test_getAddonsByID_rta() {
360   let id = `rta:${btoa(GET_TEST.successfulIDs[0])}`.slice(0, -1);
361   GET_TEST.successfulIDs[0] = id;
362   let result = await AddonRepository.getAddonsByIDs(GET_TEST.successfulIDs);
364   check_results(result, GET_RESULTS);
367 add_task(
368   {
369     pref_set: [[PREF_GET_BROWSER_MAPPINGS, GET_BROWSER_MAPPINGS_URL]],
370   },
371   async function test_getMappedAddons() {
372     const {
373       addons: result,
374       matchedIDs,
375       unmatchedIDs,
376     } = await AddonRepository.getMappedAddons("valid-browser-id", [
377       "browser-extension-test-1",
378       "browser-extension-test-2",
379       // This one is mapped but the search API won't return any data.
380       "browser-extension-test-3",
381       "browser-extension-test-4",
382       // These ones are not mapped to any Firefox add-ons.
383       "browser-extension-test-5",
384       "browser-extension-test-6",
385     ]);
386     Assert.equal(result.length, 3, "expected 3 mapped add-ons");
387     check_results(result, GET_RESULTS);
388     Assert.deepEqual(matchedIDs, [
389       "browser-extension-test-1",
390       "browser-extension-test-2",
391       "browser-extension-test-3",
392       "browser-extension-test-4",
393     ]);
394     Assert.deepEqual(unmatchedIDs, [
395       "browser-extension-test-5",
396       "browser-extension-test-6",
397     ]);
398   }
401 add_task(
402   {
403     pref_set: [[PREF_GET_BROWSER_MAPPINGS, GET_BROWSER_MAPPINGS_URL]],
404   },
405   async function test_getMappedAddons_empty_list_of_ids() {
406     const {
407       addons: result,
408       matchedIDs,
409       unmatchedIDs,
410     } = await AddonRepository.getMappedAddons("valid-browser-id", []);
411     Assert.equal(result.length, 0, "expected 0 mapped add-ons");
412     Assert.equal(matchedIDs.length, 0, "expected 0 matched IDs");
413     Assert.equal(unmatchedIDs.length, 0, "expected 0 unmatched IDs");
414   }
417 add_task(
418   {
419     pref_set: [[PREF_GET_BROWSER_MAPPINGS, GET_BROWSER_MAPPINGS_URL]],
420   },
421   async function test_getMappedAddons_invalid_ids() {
422     const {
423       addons: result,
424       matchedIDs,
425       unmatchedIDs,
426     } = await AddonRepository.getMappedAddons("valid-browser-id", [
427       "",
428       null,
429       undefined,
430     ]);
431     Assert.equal(result.length, 0, "expected 0 mapped add-ons");
432     Assert.equal(matchedIDs.length, 0, "expected 0 matched IDs");
433     Assert.deepEqual(unmatchedIDs, ["", null, undefined]);
434   }
437 add_task(
438   {
439     pref_set: [[PREF_GET_BROWSER_MAPPINGS, GET_BROWSER_MAPPINGS_URL]],
440   },
441   async function test_getMappedAddons_empty_mapping() {
442     const {
443       addons: result,
444       matchedIDs,
445       unmatchedIDs,
446     } = await AddonRepository.getMappedAddons("browser-id-empty-results", [
447       "browser-extension-test-1",
448       "browser-extension-test-2",
449       "browser-extension-test-3",
450     ]);
451     Assert.equal(result.length, 0, "expected no mapped add-ons");
452     Assert.equal(matchedIDs.length, 0, "expected 0 matched IDs");
453     Assert.equal(unmatchedIDs.length, 3, "expected 3 unmatched IDs");
454   }
457 add_task(
458   {
459     pref_set: [
460       [
461         PREF_GET_BROWSER_MAPPINGS,
462         `${BASE_URL}/browser-mappings/with-paging/%BROWSER%/?1`,
463       ],
464     ],
465   },
466   async function test_getMappedAddons_with_paging() {
467     const {
468       addons: result,
469       matchedIDs,
470       unmatchedIDs,
471     } = await AddonRepository.getMappedAddons("valid-browser-id", [
472       "browser-extension-test-1",
473       "browser-extension-test-2",
474       // This one is mapped but the search API won't return any data.
475       "browser-extension-test-3",
476       "browser-extension-test-4",
477     ]);
478     Assert.equal(result.length, 3, "expected 3 mapped add-ons");
479     check_results(result, GET_RESULTS);
480     Assert.deepEqual(matchedIDs, [
481       "browser-extension-test-1",
482       "browser-extension-test-2",
483       "browser-extension-test-3",
484       "browser-extension-test-4",
485     ]);
486     Assert.deepEqual(unmatchedIDs, []);
487   }