summaryrefslogtreecommitdiffstats
path: root/browser/components/customizableui/test/CustomizableUITestUtils.sys.mjs
blob: 2cb4e13f99e71bef778f2511a3d0048597c9baca (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

/**
 * Shared functions generally available for tests involving PanelMultiView and
 * the CustomizableUI elements in the browser window.
 */

import { Assert } from "resource://testing-common/Assert.sys.mjs";

import { BrowserTestUtils } from "resource://testing-common/BrowserTestUtils.sys.mjs";
import { TestUtils } from "resource://testing-common/TestUtils.sys.mjs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  CustomizableUI: "resource:///modules/CustomizableUI.sys.mjs",
});

export class CustomizableUITestUtils {
  /**
   * Constructs an instance that operates with the specified browser window.
   */
  constructor(window) {
    this.window = window;
    this.document = window.document;
    this.PanelUI = window.PanelUI;
  }

  /**
   * Opens a closed PanelMultiView via the specified function while waiting for
   * the main view with the specified ID to become fully interactive.
   */
  async openPanelMultiView(panel, mainView, openFn) {
    if (panel.state == "open") {
      // Some tests may intermittently leave the panel open. We report this, but
      // don't fail so we don't introduce new intermittent test failures.
      Assert.ok(
        true,
        "A previous test left the panel open. This should be" +
          " fixed, but we can still do a best-effort recovery and" +
          " assume that the requested view will be made visible."
      );
      await openFn();
      return;
    }

    if (panel.state == "hiding") {
      // There may still be tests that don't wait after invoking a command that
      // causes the main menu panel to close. Depending on timing, the panel may
      // or may not be fully closed when the following test runs. We handle this
      // case gracefully so we don't risk introducing new intermittent test
      // failures that may show up at a later time.
      Assert.ok(
        true,
        "A previous test requested the panel to close but" +
          " didn't wait for the operation to complete. While" +
          " the test should be fixed, we can still continue."
      );
    } else {
      Assert.equal(panel.state, "closed", "The panel is closed to begin with.");
    }

    let promiseShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
    await openFn();
    await promiseShown;
  }

  /**
   * Closes an open PanelMultiView via the specified function while waiting for
   * the operation to complete.
   */
  async hidePanelMultiView(panel, closeFn) {
    Assert.ok(panel.state == "open", "The panel is open to begin with.");

    let promiseHidden = BrowserTestUtils.waitForEvent(panel, "popuphidden");
    await closeFn();
    await promiseHidden;
  }

  /**
   * Opens the main menu and waits for it to become fully interactive.
   */
  async openMainMenu() {
    await this.openPanelMultiView(
      this.PanelUI.panel,
      this.PanelUI.mainView,
      () => this.PanelUI.show()
    );
  }

  /**
   * Closes the main menu and waits for the operation to complete.
   */
  async hideMainMenu() {
    await this.hidePanelMultiView(this.PanelUI.panel, () =>
      this.PanelUI.hide()
    );
  }

  /**
   * Add the search bar into the nav bar and verify it does not overflow.
   *
   * @returns {Promise}
   * @resolves The search bar element.
   * @rejects If search bar is not found, or overflows.
   */
  async addSearchBar() {
    lazy.CustomizableUI.addWidgetToArea(
      "search-container",
      lazy.CustomizableUI.AREA_NAVBAR,
      lazy.CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1
    );

    // addWidgetToArea adds the search bar into the nav bar first.  If the
    // search bar overflows, OverflowableToolbar for the nav bar moves the
    // search bar into the overflow panel in its overflow event handler
    // asynchronously.
    //
    // We should first wait for the layout flush to make sure either the search
    // bar fits into the nav bar, or overflow event gets dispatched and the
    // overflow event handler is called.
    await this.window.promiseDocumentFlushed(() => {});

    // Check if the OverflowableToolbar is handling the overflow event.
    let navbar = this.window.document.getElementById(
      lazy.CustomizableUI.AREA_NAVBAR
    );
    await TestUtils.waitForCondition(() => {
      return !navbar.overflowable.isHandlingOverflow();
    });

    let searchbar = this.window.document.getElementById("searchbar");
    if (!searchbar) {
      throw new Error("The search bar should exist.");
    }

    // If the search bar overflows, it's placed inside the overflow panel.
    //
    // We cannot use navbar's property to check if overflow happens, since it
    // can be different widget than the search bar that overflows.
    if (searchbar.closest("#widget-overflow")) {
      throw new Error(
        "The search bar should not overflow from the nav bar. " +
          "This test fails if the screen resolution is small and " +
          "the search bar overflows from the nav bar."
      );
    }

    return searchbar;
  }

  removeSearchBar() {
    lazy.CustomizableUI.removeWidgetFromArea("search-container");
  }
}