summaryrefslogtreecommitdiffstats
path: root/browser/components/uitour/test/browser_UITour.js
blob: 964be0215f353a60576296903309e462f2935d2a (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

var gTestTab;
var gContentAPI;
var gContentWindow;

Components.utils.import("resource://testing-common/TelemetryArchiveTesting.jsm", this);

function test() {
  UITourTest();
}

var tests = [
  function test_untrusted_host(done) {
    loadUITourTestPage(function() {
      let bookmarksMenu = document.getElementById("bookmarks-menu-button");
      is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");

      gContentAPI.showMenu("bookmarks");
      is(bookmarksMenu.open, false, "Bookmark menu should not open on a untrusted host");

      done();
    }, "http://mochi.test:8888/");
  },
  function test_testing_host(done) {
    // Add two testing origins intentionally surrounded by whitespace to be ignored.
    Services.prefs.setCharPref("browser.uitour.testingOrigins",
                               "https://test1.example.org, https://test2.example.org:443 ");

    registerCleanupFunction(() => {
      Services.prefs.clearUserPref("browser.uitour.testingOrigins");
    });
    function callback(result) {
      ok(result, "Callback should be called on a testing origin");
      done();
    }

    loadUITourTestPage(function() {
      gContentAPI.getConfiguration("appinfo", callback);
    }, "https://test2.example.org/");
  },
  function test_unsecure_host(done) {
    loadUITourTestPage(function() {
      let bookmarksMenu = document.getElementById("bookmarks-menu-button");
      is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");

      gContentAPI.showMenu("bookmarks");
      is(bookmarksMenu.open, false, "Bookmark menu should not open on a unsecure host");

      done();
    }, "http://example.org/");
  },
  function test_unsecure_host_override(done) {
    Services.prefs.setBoolPref("browser.uitour.requireSecure", false);
    loadUITourTestPage(function() {
      let highlight = document.getElementById("UITourHighlight");
      is_element_hidden(highlight, "Highlight should initially be hidden");

      gContentAPI.showHighlight("urlbar");
      waitForElementToBeVisible(highlight, done, "Highlight should be shown on a unsecure host when override pref is set");

      Services.prefs.setBoolPref("browser.uitour.requireSecure", true);
    }, "http://example.org/");
  },
  function test_disabled(done) {
    Services.prefs.setBoolPref("browser.uitour.enabled", false);

    let bookmarksMenu = document.getElementById("bookmarks-menu-button");
    is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");

    gContentAPI.showMenu("bookmarks");
    is(bookmarksMenu.open, false, "Bookmark menu should not open when feature is disabled");

    Services.prefs.setBoolPref("browser.uitour.enabled", true);
    done();
  },
  function test_highlight(done) {
    function test_highlight_2() {
      let highlight = document.getElementById("UITourHighlight");
      gContentAPI.hideHighlight();

      waitForElementToBeHidden(highlight, test_highlight_3, "Highlight should be hidden after hideHighlight()");
    }
    function test_highlight_3() {
      is_element_hidden(highlight, "Highlight should be hidden after hideHighlight()");

      gContentAPI.showHighlight("urlbar");
      waitForElementToBeVisible(highlight, test_highlight_4, "Highlight should be shown after showHighlight()");
    }
    function test_highlight_4() {
      let highlight = document.getElementById("UITourHighlight");
      gContentAPI.showHighlight("backForward");
      waitForElementToBeVisible(highlight, done, "Highlight should be shown after showHighlight()");
    }

    let highlight = document.getElementById("UITourHighlight");
    is_element_hidden(highlight, "Highlight should initially be hidden");

    gContentAPI.showHighlight("urlbar");
    waitForElementToBeVisible(highlight, test_highlight_2, "Highlight should be shown after showHighlight()");
  },
  function test_highlight_circle(done) {
    function check_highlight_size() {
      let panel = highlight.parentElement;
      let anchor = panel.anchorNode;
      let anchorRect = anchor.getBoundingClientRect();
      info("addons target: width: " + anchorRect.width + " height: " + anchorRect.height);
      let maxDimension = Math.round(Math.max(anchorRect.width, anchorRect.height));
      let highlightRect = highlight.getBoundingClientRect();
      info("highlight: width: " + highlightRect.width + " height: " + highlightRect.height);
      is(Math.round(highlightRect.width), maxDimension, "The width of the highlight should be equal to the largest dimension of the target");
      is(Math.round(highlightRect.height), maxDimension, "The height of the highlight should be equal to the largest dimension of the target");
      is(Math.round(highlightRect.height), Math.round(highlightRect.width), "The height and width of the highlight should be the same to create a circle");
      is(highlight.style.borderRadius, "100%", "The border-radius should be 100% to create a circle");
      done();
    }
    let highlight = document.getElementById("UITourHighlight");
    is_element_hidden(highlight, "Highlight should initially be hidden");

    gContentAPI.showHighlight("addons");
    waitForElementToBeVisible(highlight, check_highlight_size, "Highlight should be shown after showHighlight()");
  },
  function test_highlight_customize_auto_open_close(done) {
    let highlight = document.getElementById("UITourHighlight");
    gContentAPI.showHighlight("customize");
    waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
      isnot(PanelUI.panel.state, "closed", "Panel should have opened");

      // Move the highlight outside which should close the app menu.
      gContentAPI.showHighlight("appMenu");
      waitForElementToBeVisible(highlight, function checkPanelIsClosed() {
        isnot(PanelUI.panel.state, "open",
              "Panel should have closed after the highlight moved elsewhere.");
        done();
      }, "Highlight should move to the appMenu button");
    }, "Highlight should be shown after showHighlight() for fixed panel items");
  },
  function test_highlight_customize_manual_open_close(done) {
    let highlight = document.getElementById("UITourHighlight");
    // Manually open the app menu then show a highlight there. The menu should remain open.
    let shownPromise = promisePanelShown(window);
    gContentAPI.showMenu("appMenu");
    shownPromise.then(() => {
      isnot(PanelUI.panel.state, "closed", "Panel should have opened");
      gContentAPI.showHighlight("customize");

      waitForElementToBeVisible(highlight, function checkPanelIsStillOpen() {
        isnot(PanelUI.panel.state, "closed", "Panel should still be open");

        // Move the highlight outside which shouldn't close the app menu since it was manually opened.
        gContentAPI.showHighlight("appMenu");
        waitForElementToBeVisible(highlight, function () {
          isnot(PanelUI.panel.state, "closed",
                "Panel should remain open since UITour didn't open it in the first place");
          gContentAPI.hideMenu("appMenu");
          done();
        }, "Highlight should move to the appMenu button");
      }, "Highlight should be shown after showHighlight() for fixed panel items");
    }).then(null, Components.utils.reportError);
  },
  function test_highlight_effect(done) {
    function waitForHighlightWithEffect(highlightEl, effect, next, error) {
      return waitForCondition(() => highlightEl.getAttribute("active") == effect,
                              next,
                              error);
    }
    function checkDefaultEffect() {
      is(highlight.getAttribute("active"), "none", "The default should be no effect");

      gContentAPI.showHighlight("urlbar", "none");
      waitForHighlightWithEffect(highlight, "none", checkZoomEffect, "There should be no effect");
    }
    function checkZoomEffect() {
      gContentAPI.showHighlight("urlbar", "zoom");
      waitForHighlightWithEffect(highlight, "zoom", () => {
        let style = window.getComputedStyle(highlight);
        is(style.animationName, "uitour-zoom", "The animation-name should be uitour-zoom");
        checkSameEffectOnDifferentTarget();
      }, "There should be a zoom effect");
    }
    function checkSameEffectOnDifferentTarget() {
      gContentAPI.showHighlight("appMenu", "wobble");
      waitForHighlightWithEffect(highlight, "wobble", () => {
        highlight.addEventListener("animationstart", function onAnimationStart(aEvent) {
          highlight.removeEventListener("animationstart", onAnimationStart);
          ok(true, "Animation occurred again even though the effect was the same");
          checkRandomEffect();
        });
        gContentAPI.showHighlight("backForward", "wobble");
      }, "There should be a wobble effect");
    }
    function checkRandomEffect() {
      function waitForActiveHighlight(highlightEl, next, error) {
        return waitForCondition(() => highlightEl.hasAttribute("active"),
                                next,
                                error);
      }

      gContentAPI.hideHighlight();
      gContentAPI.showHighlight("urlbar", "random");
      waitForActiveHighlight(highlight, () => {
        ok(highlight.hasAttribute("active"), "The highlight should be active");
        isnot(highlight.getAttribute("active"), "none", "A random effect other than none should have been chosen");
        isnot(highlight.getAttribute("active"), "random", "The random effect shouldn't be 'random'");
        isnot(UITour.highlightEffects.indexOf(highlight.getAttribute("active")), -1, "Check that a supported effect was randomly chosen");
        done();
      }, "There should be an active highlight with a random effect");
    }

    let highlight = document.getElementById("UITourHighlight");
    is_element_hidden(highlight, "Highlight should initially be hidden");

    gContentAPI.showHighlight("urlbar");
    waitForElementToBeVisible(highlight, checkDefaultEffect, "Highlight should be shown after showHighlight()");
  },
  function test_highlight_effect_unsupported(done) {
    function checkUnsupportedEffect() {
      is(highlight.getAttribute("active"), "none", "No effect should be used when an unsupported effect is requested");
      done();
    }

    let highlight = document.getElementById("UITourHighlight");
    is_element_hidden(highlight, "Highlight should initially be hidden");

    gContentAPI.showHighlight("urlbar", "__UNSUPPORTED__");
    waitForElementToBeVisible(highlight, checkUnsupportedEffect, "Highlight should be shown after showHighlight()");
  },
  function test_info_1(done) {
    let popup = document.getElementById("UITourTooltip");
    let title = document.getElementById("UITourTooltipTitle");
    let desc = document.getElementById("UITourTooltipDescription");
    let icon = document.getElementById("UITourTooltipIcon");
    let buttons = document.getElementById("UITourTooltipButtons");

    popup.addEventListener("popupshown", function onPopupShown() {
      popup.removeEventListener("popupshown", onPopupShown);
      is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
      is(title.textContent, "test title", "Popup should have correct title");
      is(desc.textContent, "test text", "Popup should have correct description text");
      is(icon.src, "", "Popup should have no icon");
      is(buttons.hasChildNodes(), false, "Popup should have no buttons");

      popup.addEventListener("popuphidden", function onPopupHidden() {
        popup.removeEventListener("popuphidden", onPopupHidden);

        popup.addEventListener("popupshown", function onPopupShown() {
          popup.removeEventListener("popupshown", onPopupShown);
          done();
        });

        gContentAPI.showInfo("urlbar", "test title", "test text");

      });
      gContentAPI.hideInfo();
    });

    gContentAPI.showInfo("urlbar", "test title", "test text");
  },
  taskify(function* test_info_2() {
    let popup = document.getElementById("UITourTooltip");
    let title = document.getElementById("UITourTooltipTitle");
    let desc = document.getElementById("UITourTooltipDescription");
    let icon = document.getElementById("UITourTooltipIcon");
    let buttons = document.getElementById("UITourTooltipButtons");

    yield showInfoPromise("urlbar", "urlbar title", "urlbar text");

    is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
    is(title.textContent, "urlbar title", "Popup should have correct title");
    is(desc.textContent, "urlbar text", "Popup should have correct description text");
    is(icon.src, "", "Popup should have no icon");
    is(buttons.hasChildNodes(), false, "Popup should have no buttons");

    yield showInfoPromise("search", "search title", "search text");

    is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
    is(title.textContent, "search title", "Popup should have correct title");
    is(desc.textContent, "search text", "Popup should have correct description text");
  }),
  function test_getConfigurationVersion(done) {
    function callback(result) {
      let props = ["defaultUpdateChannel", "version"];
      for (let property of props) {
        ok(typeof(result[property]) !== "undefined", "Check " + property + " isn't undefined.");
        is(result[property], Services.appinfo[property], "Should have the same " + property + " property.");
      }
      done();
    }

    gContentAPI.getConfiguration("appinfo", callback);
  },
  function test_getConfigurationDistribution(done) {
    gContentAPI.getConfiguration("appinfo", (result) => {
      ok(typeof(result.distribution) !== "undefined", "Check distribution isn't undefined.");
      is(result.distribution, "default", "Should be \"default\" without preference set.");

      let defaults = Services.prefs.getDefaultBranch("distribution.");
      let testDistributionID = "TestDistribution";
      defaults.setCharPref("id", testDistributionID);
      gContentAPI.getConfiguration("appinfo", (result) => {
        ok(typeof(result.distribution) !== "undefined", "Check distribution isn't undefined.");
        is(result.distribution, testDistributionID, "Should have the distribution as set in preference.");

        done();
      });
    });
  },
  function test_addToolbarButton(done) {
    let placement = CustomizableUI.getPlacementOfWidget("panic-button");
    is(placement, null, "default UI has panic button in the palette");

    gContentAPI.getConfiguration("availableTargets", (data) => {
      let available = (data.targets.indexOf("forget") != -1);
      ok(!available, "Forget button should not be available by default");

      gContentAPI.addNavBarWidget("forget", () => {
        info("addNavBarWidget callback successfully called");

        let placement = CustomizableUI.getPlacementOfWidget("panic-button");
        is(placement.area, CustomizableUI.AREA_NAVBAR);

        gContentAPI.getConfiguration("availableTargets", (data) => {
          let available = (data.targets.indexOf("forget") != -1);
          ok(available, "Forget button should now be available");

          // Cleanup
          CustomizableUI.removeWidgetFromArea("panic-button");
          done();
        });
      });
    });
  },
  function test_search(done) {
    Services.search.init(rv => {
      if (!Components.isSuccessCode(rv)) {
        ok(false, "search service init failed: " + rv);
        done();
        return;
      }
      let defaultEngine = Services.search.defaultEngine;
      gContentAPI.getConfiguration("search", data => {
        let visibleEngines = Services.search.getVisibleEngines();
        let expectedEngines = visibleEngines.filter((engine) => engine.identifier)
                                            .map((engine) => "searchEngine-" + engine.identifier);

        let engines = data.engines;
        ok(Array.isArray(engines), "data.engines should be an array");
        is(engines.sort().toString(), expectedEngines.sort().toString(),
           "Engines should be as expected");

        is(data.searchEngineIdentifier, defaultEngine.identifier,
           "the searchEngineIdentifier property should contain the defaultEngine's identifier");

        let someOtherEngineID = data.engines.filter(t => t != "searchEngine-" + defaultEngine.identifier)[0];
        someOtherEngineID = someOtherEngineID.replace(/^searchEngine-/, "");

        let observe = function (subject, topic, verb) {
          info("browser-search-engine-modified: " + verb);
          if (verb == "engine-current") {
            is(Services.search.defaultEngine.identifier, someOtherEngineID, "correct engine was switched to");
            done();
          }
        };
        Services.obs.addObserver(observe, "browser-search-engine-modified", false);
        registerCleanupFunction(() => {
          // Clean up
          Services.obs.removeObserver(observe, "browser-search-engine-modified");
          Services.search.defaultEngine = defaultEngine;
        });

        gContentAPI.setDefaultSearchEngine(someOtherEngineID);
      });
    });
  },
  taskify(function* test_treatment_tag() {
    let ac = new TelemetryArchiveTesting.Checker();
    yield ac.promiseInit();
    yield gContentAPI.setTreatmentTag("foobar", "baz");
    // Wait until the treatment telemetry is sent before looking in the archive.
    yield BrowserTestUtils.waitForContentEvent(gTestTab.linkedBrowser, "mozUITourNotification", false,
                                               event => event.detail.event === "TreatmentTag:TelemetrySent");
    yield new Promise((resolve) => {
      gContentAPI.getTreatmentTag("foobar", (data) => {
        is(data.value, "baz", "set and retrieved treatmentTag");
        ac.promiseFindPing("uitour-tag", [
          [["payload", "tagName"], "foobar"],
          [["payload", "tagValue"], "baz"],
        ]).then((found) => {
          ok(found, "Telemetry ping submitted for setTreatmentTag");
          resolve();
        }, (err) => {
          ok(false, "Exception finding uitour telemetry ping: " + err);
          resolve();
        });
      });
    });
  }),

  // Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down.
  taskify(function* cleanupMenus() {
    let shownPromise = promisePanelShown(window);
    gContentAPI.showMenu("appMenu");
    yield shownPromise;
  }),
];