summaryrefslogtreecommitdiffstats
path: root/toolkit/components/webextensions/test/xpcshell/test_ext_experiments.js
blob: bc6bfcd68b7f90e5cdcfe4f5bab25aa7e7b72f6f (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
"use strict";

/* globals browser */

XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                  "resource://gre/modules/AddonManager.jsm");

function promiseAddonStartup() {
  const {Management} = Cu.import("resource://gre/modules/Extension.jsm");

  return new Promise(resolve => {
    let listener = (evt, extension) => {
      Management.off("startup", listener);
      resolve(extension);
    };

    Management.on("startup", listener);
  });
}

add_task(function* setup() {
  yield ExtensionTestUtils.startAddonManager();
});

add_task(function* test_experiments_api() {
  let apiAddonFile = Extension.generateZipFile({
    "install.rdf": `<?xml version="1.0" encoding="UTF-8"?>
      <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
           xmlns:em="http://www.mozilla.org/2004/em-rdf#">
          <Description about="urn:mozilla:install-manifest"
              em:id="meh@experiments.addons.mozilla.org"
              em:name="Meh Experiment"
              em:type="256"
              em:version="0.1"
              em:description="Meh experiment"
              em:creator="Mozilla">

              <em:targetApplication>
                  <Description
                      em:id="xpcshell@tests.mozilla.org"
                      em:minVersion="48"
                      em:maxVersion="*"/>
              </em:targetApplication>
          </Description>
      </RDF>
    `,

    "api.js": String.raw`
      Components.utils.import("resource://gre/modules/Services.jsm");

      Services.obs.notifyObservers(null, "webext-api-loaded", "");

      class API extends ExtensionAPI {
        getAPI(context) {
          return {
            meh: {
              hello(text) {
                Services.obs.notifyObservers(null, "webext-api-hello", text);
              }
            }
          }
        }
      }
    `,

    "schema.json": [
      {
        "namespace": "meh",
        "description": "All full of meh.",
        "permissions": ["experiments.meh"],
        "functions": [
          {
            "name": "hello",
            "type": "function",
            "description": "Hates you. This is all.",
            "parameters": [
              {"type": "string", "name": "text"},
            ],
          },
        ],
      },
    ],
  });

  let addonFile = Extension.generateXPI({
    manifest: {
      applications: {gecko: {id: "meh@web.extension"}},
      permissions: ["experiments.meh"],
    },

    background() {
      // The test code below checks that hello() is called at the right
      // time with the string "Here I am".  Verify that the api schema is
      // being correctly interpreted by calling hello() with bad arguments
      // and only calling hello() with the magic string if the call with
      // bad arguments throws.
      try {
        browser.meh.hello("I should not see this", "since two arguments are bad");
      } catch (err) {
        browser.meh.hello("Here I am");
      }
    },
  });

  let boringAddonFile = Extension.generateXPI({
    manifest: {
      applications: {gecko: {id: "boring@web.extension"}},
    },
    background() {
      if (browser.meh) {
        browser.meh.hello("Here I should not be");
      }
    },
  });

  do_register_cleanup(() => {
    for (let file of [apiAddonFile, addonFile, boringAddonFile]) {
      Services.obs.notifyObservers(file, "flush-cache-entry", null);
      file.remove(false);
    }
  });


  let resolveHello;
  let observer = (subject, topic, data) => {
    if (topic == "webext-api-loaded") {
      ok(!!resolveHello, "Should not see API loaded until dependent extension loads");
    } else if (topic == "webext-api-hello") {
      resolveHello(data);
    }
  };

  Services.obs.addObserver(observer, "webext-api-loaded", false);
  Services.obs.addObserver(observer, "webext-api-hello", false);
  do_register_cleanup(() => {
    Services.obs.removeObserver(observer, "webext-api-loaded");
    Services.obs.removeObserver(observer, "webext-api-hello");
  });


  // Install API add-on.
  let apiAddon = yield AddonManager.installTemporaryAddon(apiAddonFile);

  let {APIs} = Cu.import("resource://gre/modules/ExtensionManagement.jsm", {});
  ok(APIs.apis.has("meh"), "Should have meh API.");


  // Install boring WebExtension add-on.
  let boringAddon = yield AddonManager.installTemporaryAddon(boringAddonFile);
  yield promiseAddonStartup();


  // Install interesting WebExtension add-on.
  let promise = new Promise(resolve => {
    resolveHello = resolve;
  });

  let addon = yield AddonManager.installTemporaryAddon(addonFile);
  yield promiseAddonStartup();

  let hello = yield promise;
  equal(hello, "Here I am", "Should get hello from add-on");

  // Cleanup.
  apiAddon.uninstall();

  boringAddon.userDisabled = true;
  yield new Promise(do_execute_soon);

  equal(addon.appDisabled, true, "Add-on should be app-disabled after its dependency is removed.");

  addon.uninstall();
  boringAddon.uninstall();
});