summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/sidebar/browser_sidebar_switcher.js
blob: 032c23b029921bfc03f78b42a442e47a5a3dd52e (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
registerCleanupFunction(() => {
  SidebarUI.hide();
});

/**
 * Helper function that opens a sidebar switcher panel popup menu
 * @returns Promise that resolves when the switcher panel popup is shown
 *          without any action from a user/test
 */
function showSwitcherPanelPromise() {
  return new Promise(resolve => {
    SidebarUI._switcherPanel.addEventListener(
      "popupshown",
      () => {
        resolve();
      },
      { once: true }
    );
    SidebarUI.showSwitcherPanel();
  });
}

/**
 * Helper function that waits for a sidebar switcher panel's "popupshown" event
 * @returns Promise which resolves when the popup menu is opened
 */
async function waitForSwitcherPopupShown() {
  return BrowserTestUtils.waitForEvent(SidebarUI._switcherPanel, "popupshown");
}

/**
 * Helper function that sends a mouse click to a specific menu item or a key
 * event to a active menu item of the sidebar switcher menu popup. Provide a
 * querySelector parameter when a click behavior is needed.
 * @param {String} [querySelector=null]  An HTML attribute of the menu item
 *                                       to be clicked
 * @returns Promise that resolves when both the menu popup is hidden and
 *          the sidebar itself is focused
 */
function pickSwitcherMenuitem(querySelector = null) {
  let sidebarPopup = document.querySelector("#sidebarMenu-popup");
  let hideSwitcherPanelPromise = Promise.all([
    BrowserTestUtils.waitForEvent(window, "SidebarFocused"),
    BrowserTestUtils.waitForEvent(sidebarPopup, "popuphidden"),
  ]);
  if (querySelector) {
    document.querySelector(querySelector).click();
  } else {
    EventUtils.synthesizeKey("KEY_Enter", {});
  }
  return hideSwitcherPanelPromise;
}

/**
 * Helper function to test a key handling of sidebar menu popup items used to
 * access a specific sidebar
 * @param {String} key           Event.key to open the switcher menu popup
 * @param {String} sidebarTitle  Title of the sidebar that is to be activated
 *                               during the test (capitalized one word versions),
 *                               i.e. "History" or "Tabs"
 */
async function testSidebarMenuKeyToggle(key, sidebarTitle) {
  info(`Testing "${key}" key handling of sidebar menu popup items
  to access ${sidebarTitle} sidebar`);

  Assert.ok(SidebarUI.isOpen, "Sidebar is open");

  let sidebarSwitcher = document.querySelector("#sidebar-switcher-target");
  let sidebar = document.getElementById("sidebar");
  let searchBox = sidebar.firstElementChild;

  // If focus is on the search field (i.e. on the History sidebar),
  // or if the focus is on the awesomebar (bug 1835899),
  // move it to the switcher target:

  if (searchBox && searchBox.matches(":focus")) {
    EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true, repeat: 2 });
  } else if (!sidebarSwitcher.matches(":focus")) {
    sidebarSwitcher.focus();
  }

  Assert.equal(
    document.activeElement,
    sidebarSwitcher,
    "The sidebar switcher target button is focused"
  );
  Assert.ok(
    sidebarSwitcher.matches(":focus"),
    "The sidebar switcher target button is focused"
  );
  Assert.equal(
    SidebarUI._switcherPanel.state,
    "closed",
    "Sidebar menu popup is closed"
  );

  let promisePopupShown = waitForSwitcherPopupShown();

  // Activate sidebar switcher target to open its sidebar menu popup:
  EventUtils.synthesizeKey(key, {});

  await promisePopupShown;

  Assert.equal(
    SidebarUI._switcherPanel.state,
    "open",
    "Sidebar menu popup is open"
  );

  info("Testing keyboard navigation between sidebar menu popup controls");

  let arrowDown = async (menuitemId, msg) => {
    let menuItemActive = BrowserTestUtils.waitForEvent(
      SidebarUI._switcherPanel,
      "DOMMenuItemActive"
    );
    EventUtils.synthesizeKey("KEY_ArrowDown", {});
    await menuItemActive;
    Assert.ok(
      document.getElementById(menuitemId).hasAttribute("_moz-menuactive"),
      msg
    );
  };

  // Move to the first sidebar switcher option:
  await arrowDown(
    "sidebar-switcher-bookmarks",
    "The 1st sidebar menu item (Bookmarks) is active"
  );

  // Move to the next sidebar switcher option:
  await arrowDown(
    "sidebar-switcher-history",
    "The 2nd sidebar menu item (History) is active"
  );

  if (sidebarTitle === "Tabs") {
    await arrowDown(
      "sidebar-switcher-tabs",
      "The 3rd sidebar menu item (Synced Tabs) is active"
    );
  }

  // Activate the tested sidebar switcher option to open the tested sidebar:
  let sidebarShown = BrowserTestUtils.waitForEvent(window, "SidebarShown");
  await pickSwitcherMenuitem(/* querySelector = */ null);
  await sidebarShown;

  info("Testing keyboard navigation when a sidebar menu popup is closed");

  Assert.equal(
    SidebarUI._switcherPanel.state,
    "closed",
    "Sidebar menu popup is closed"
  );
  // Test the sidebar panel is updated
  Assert.equal(
    SidebarUI._box.getAttribute("sidebarcommand"),
    `view${sidebarTitle}Sidebar` /* e.g. "viewHistorySidebar" */,
    `${sidebarTitle} sidebar loaded`
  );
  Assert.equal(
    SidebarUI.currentID,
    `view${sidebarTitle}Sidebar` /* e.g. "viewHistorySidebar" */,
    `${sidebarTitle}'s current ID is updated to a target view`
  );
}

add_task(async function markup() {
  // If a sidebar is already open, close it.
  if (!document.getElementById("sidebar-box").hidden) {
    Assert.ok(
      false,
      "Unexpected sidebar found - a previous test failed to cleanup correctly"
    );
    SidebarUI.hide();
  }

  let sidebarPopup = document.querySelector("#sidebarMenu-popup");
  let sidebarSwitcher = document.querySelector("#sidebar-switcher-target");
  let sidebarTitle = document.querySelector("#sidebar-title");

  info("Test default markup of the sidebar switcher control");

  Assert.equal(
    sidebarSwitcher.tagName,
    "toolbarbutton",
    "Sidebar switcher target control is a toolbarbutton"
  );
  Assert.equal(
    sidebarSwitcher.children[1],
    sidebarTitle,
    "Sidebar switcher target control has a child label element"
  );
  Assert.equal(
    sidebarTitle.tagName,
    "label",
    "Sidebar switcher target control has a label element (that is expected to provide its accessible name"
  );
  Assert.equal(
    sidebarSwitcher.getAttribute("aria-expanded"),
    "false",
    "Sidebar switcher button is collapsed by default"
  );

  info("Test dynamic changes in the markup of the sidebar switcher control");

  await SidebarUI.show("viewBookmarksSidebar");
  await showSwitcherPanelPromise();

  Assert.equal(
    sidebarSwitcher.getAttribute("aria-expanded"),
    "true",
    "Sidebar switcher button is expanded when a sidebar menu is shown"
  );

  let waitForPopupHidden = BrowserTestUtils.waitForEvent(
    sidebarPopup,
    "popuphidden"
  );

  // Close on Escape anywhere
  EventUtils.synthesizeKey("KEY_Escape", {});
  await waitForPopupHidden;

  Assert.equal(
    sidebarSwitcher.getAttribute("aria-expanded"),
    "false",
    "Sidebar switcher button is collapsed when a sidebar menu is dismissed"
  );

  SidebarUI.hide();
});

add_task(async function keynav() {
  // If a sidebar is already open, close it.
  if (SidebarUI.isOpen) {
    Assert.ok(
      false,
      "Unexpected sidebar found - a previous test failed to cleanup correctly"
    );
    SidebarUI.hide();
  }

  await SidebarUI.show("viewBookmarksSidebar");

  await testSidebarMenuKeyToggle("KEY_Enter", "History");
  await testSidebarMenuKeyToggle(" ", "Tabs");

  SidebarUI.hide();
});

add_task(async function mouse() {
  // If a sidebar is already open, close it.
  if (!document.getElementById("sidebar-box").hidden) {
    Assert.ok(
      false,
      "Unexpected sidebar found - a previous test failed to cleanup correctly"
    );
    SidebarUI.hide();
  }

  let sidebar = document.querySelector("#sidebar-box");
  await SidebarUI.show("viewBookmarksSidebar");

  await showSwitcherPanelPromise();
  await pickSwitcherMenuitem("#sidebar-switcher-history");
  Assert.equal(
    sidebar.getAttribute("sidebarcommand"),
    "viewHistorySidebar",
    "History sidebar loaded"
  );

  await showSwitcherPanelPromise();
  await pickSwitcherMenuitem("#sidebar-switcher-tabs");
  Assert.equal(
    sidebar.getAttribute("sidebarcommand"),
    "viewTabsSidebar",
    "Tabs sidebar loaded"
  );

  await showSwitcherPanelPromise();
  await pickSwitcherMenuitem("#sidebar-switcher-bookmarks");
  Assert.equal(
    sidebar.getAttribute("sidebarcommand"),
    "viewBookmarksSidebar",
    "Bookmarks sidebar loaded"
  );
});