|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 "use strict"; |
|
5 |
|
6 module.metadata = { |
|
7 "engines": { |
|
8 "Firefox": "*" |
|
9 } |
|
10 }; |
|
11 |
|
12 const { Toolbar } = require("sdk/ui/toolbar"); |
|
13 const { Loader } = require("sdk/test/loader"); |
|
14 const { identify } = require("sdk/ui/id"); |
|
15 const { getMostRecentBrowserWindow, open, getOuterId } = require("sdk/window/utils"); |
|
16 const { ready, close } = require("sdk/window/helpers"); |
|
17 const { defer } = require("sdk/core/promise"); |
|
18 const { send, stop, Reactor } = require("sdk/event/utils"); |
|
19 const { object } = require("sdk/util/sequence"); |
|
20 const { CustomizationInput } = require("sdk/input/customizable-ui"); |
|
21 const { OutputPort } = require("sdk/output/system"); |
|
22 const output = new OutputPort({ id: "toolbar-change" }); |
|
23 |
|
24 const wait = (toolbar, event) => { |
|
25 let { promise, resolve } = defer(); |
|
26 toolbar.once(event, resolve); |
|
27 return promise; |
|
28 }; |
|
29 |
|
30 const show = ({id}) => send(output, object([id, {collapsed: false}])); |
|
31 const hide = ({id}) => send(output, object([id, {collapsed: true}])); |
|
32 const retitle = ({id}, title) => send(output, object([id, {title: title}])); |
|
33 |
|
34 const isAttached = ({id}, window=getMostRecentBrowserWindow()) => |
|
35 !!window.document.getElementById(id); |
|
36 |
|
37 const isCollapsed = ({id}, window=getMostRecentBrowserWindow()) => |
|
38 window.document.getElementById(id).getAttribute("collapsed") === "true"; |
|
39 |
|
40 const closeViaButton = ({id}, window=getMostRecentBrowserWindow()) => |
|
41 window.document.getElementById("close-" + id).click(); |
|
42 |
|
43 const readTitle = ({id}, window=getMostRecentBrowserWindow()) => |
|
44 window.document.getElementById(id).getAttribute("toolbarname"); |
|
45 |
|
46 exports["test toolbar API"] = function*(assert) { |
|
47 assert.throws(() => new Toolbar(), |
|
48 /The `option.title`/, |
|
49 "toolbar requires title"); |
|
50 |
|
51 assert.throws(() => new Toolbar({ hidden: false }), |
|
52 /The `option.title`/, |
|
53 "toolbar requires title"); |
|
54 |
|
55 const t1 = new Toolbar({ title: "foo" }); |
|
56 |
|
57 assert.throws(() => new Toolbar({ title: "foo" }), |
|
58 /already exists/, |
|
59 "can't create identical toolbars"); |
|
60 |
|
61 assert.ok(t1.id, "toolbar has an id"); |
|
62 assert.equal(t1.id, identify(t1), "identify returns toolbar id"); |
|
63 assert.deepEqual(t1.items, [], "toolbar items are empty"); |
|
64 assert.equal(t1.title, void(0), "title is void until attached"); |
|
65 assert.equal(t1.hidden, void(0), "hidden is void until attached"); |
|
66 |
|
67 yield wait(t1, "attach"); |
|
68 |
|
69 assert.equal(t1.title, "foo", "title is set after attach"); |
|
70 assert.equal(t1.hidden, false, "by default toolbar isn't hidden"); |
|
71 |
|
72 assert.throws(() => new Toolbar({ title: "foo" }), |
|
73 /already exists/, |
|
74 "still can't create identical toolbar"); |
|
75 |
|
76 |
|
77 const t2 = new Toolbar({ title: "bar", hidden: true }); |
|
78 assert.pass("can create different toolbar though"); |
|
79 |
|
80 assert.ok(t2.id, "toolbar has an id"); |
|
81 assert.equal(t2.id, identify(t2), "identify returns toolbar id"); |
|
82 assert.notEqual(t2.id, t1.id, "each toolbar has unique id"); |
|
83 |
|
84 yield wait(t2, "attach"); |
|
85 |
|
86 assert.equal(t2.title, "bar", "title is set after attach"); |
|
87 assert.equal(t2.hidden, true, "toolbar is hidden as specified"); |
|
88 |
|
89 t2.destroy(); |
|
90 t1.destroy(); |
|
91 |
|
92 yield wait(t1, "detach"); |
|
93 |
|
94 assert.equal(t1.title, void(0), "title is voided after detach"); |
|
95 assert.equal(t1.hidden, void(0), "hidden is void fater detach"); |
|
96 |
|
97 |
|
98 const t3 = new Toolbar({ title: "foo" }); |
|
99 assert.pass("Can create toolbar after identical was detached"); |
|
100 |
|
101 assert.equal(t3.id, t1.id, "toolbar has a same id"); |
|
102 assert.equal(t3.id, identify(t3), "identify returns toolbar.id"); |
|
103 assert.equal(t3.title, void(0), "title is void before attach"); |
|
104 assert.equal(t3.hidden, void(0), "hidden is void before attach"); |
|
105 |
|
106 yield wait(t3, "attach"); |
|
107 |
|
108 assert.equal(t3.title, "foo", "title is set"); |
|
109 assert.equal(t3.hidden, false, "toolbar is hidden"); |
|
110 |
|
111 t3.destroy(); |
|
112 |
|
113 yield wait(t3, "detach"); |
|
114 }; |
|
115 |
|
116 exports["test show / hide toolbar"] = function*(assert) { |
|
117 const t1 = new Toolbar({ title: "foo" }); |
|
118 |
|
119 yield wait(t1, "attach"); |
|
120 |
|
121 assert.equal(t1.title, "foo", "title is set after attach"); |
|
122 assert.equal(t1.hidden, false, "by default toolbar isn't hidden"); |
|
123 assert.ok(isAttached(t1), "toolbar was actually attarched"); |
|
124 assert.ok(!isCollapsed(t1), "toolbar isn't collapsed"); |
|
125 |
|
126 hide(t1); |
|
127 assert.equal(t1.hidden, false, "not hidden yet"); |
|
128 |
|
129 yield wait(t1, "hide"); |
|
130 assert.equal(t1.hidden, true, "toolbar got hidden"); |
|
131 assert.ok(isCollapsed(t1), "toolbar is collapsed"); |
|
132 |
|
133 show(t1); |
|
134 |
|
135 yield wait(t1, "show"); |
|
136 assert.equal(t1.hidden, false, "toolbar got shown"); |
|
137 assert.ok(!isCollapsed(t1), "toolbar isn't collapsed"); |
|
138 |
|
139 t1.destroy(); |
|
140 yield wait(t1, "detach"); |
|
141 assert.ok(!isAttached(t1), "toolbar is no longer attached"); |
|
142 }; |
|
143 |
|
144 exports["test multiple windows & toolbars"] = function*(assert) { |
|
145 const w1 = getMostRecentBrowserWindow(); |
|
146 const t1 = new Toolbar({ title: "multi window" }); |
|
147 |
|
148 yield wait(t1, "attach"); |
|
149 |
|
150 assert.equal(t1.title, "multi window", "title is set after attach"); |
|
151 assert.equal(t1.hidden, false, "by default toolbar isn't hidden"); |
|
152 assert.ok(isAttached(t1, w1), "toolbar was actually attarched"); |
|
153 assert.ok(!isCollapsed(t1, w1), "toolbar isn't collapsed"); |
|
154 |
|
155 const w2 = open(); |
|
156 yield ready(w2); |
|
157 |
|
158 assert.ok(isAttached(t1, w2), "toolbar was attached to second window"); |
|
159 assert.ok(!isCollapsed(t1, w2), "toolbar isn't collabsed"); |
|
160 |
|
161 hide(t1); |
|
162 yield wait(t1, "hide"); |
|
163 |
|
164 assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2), |
|
165 "toolbar is collabsed"); |
|
166 |
|
167 |
|
168 const w3 = open(); |
|
169 yield ready(w3); |
|
170 |
|
171 assert.ok(isAttached(t1, w1) && isAttached(t1, w2) && isAttached(t1, w3), |
|
172 "toolbar is attached to all windows"); |
|
173 assert.ok(isCollapsed(t1, w3) && isCollapsed(t1, w3) && isCollapsed(t1, w3), |
|
174 "toolbar still collapsed in all windows"); |
|
175 |
|
176 |
|
177 const t2 = new Toolbar({ title: "multi hidden", hidden: true }); |
|
178 |
|
179 yield wait(t2, "attach"); |
|
180 |
|
181 assert.equal(t2.title, "multi hidden", "title is set after attach"); |
|
182 assert.equal(t2.hidden, true, "isn't hidden as specified"); |
|
183 |
|
184 assert.ok(isAttached(t1, w1) && isAttached(t1, w2) && isAttached(t1, w3), |
|
185 "toolbar#1 is still attached"); |
|
186 assert.ok(isAttached(t2, w1) && isAttached(t2, w2) && isAttached(t2, w3), |
|
187 "toolbar#2 was attached to all windows"); |
|
188 |
|
189 assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2) && isCollapsed(t1, w3), |
|
190 "toolbar#1 is still collapsed"); |
|
191 |
|
192 assert.ok(isCollapsed(t2, w1) && isCollapsed(t2, w2) && isCollapsed(t2, w3), |
|
193 "toolbar#2 is collapsed"); |
|
194 |
|
195 t1.destroy(); |
|
196 yield wait(t1, "detach"); |
|
197 |
|
198 assert.ok(!isAttached(t1, w1) && !isAttached(t1, w2) && !isAttached(t1, w3), |
|
199 "toolbar#1 was detached from all windows"); |
|
200 assert.ok(isAttached(t2, w1) && isAttached(t2, w2) && isAttached(t2, w3), |
|
201 "toolbar#2 is still attached to all windows"); |
|
202 |
|
203 yield close(w2); |
|
204 |
|
205 assert.ok(isAttached(t2, w1) && isAttached(t2, w3), |
|
206 "toolbar#2 is still attached to remaining windows"); |
|
207 assert.ok(isCollapsed(t2, w1) && isCollapsed(t2, w3), |
|
208 "toolbar#2 is still collapsed"); |
|
209 |
|
210 show(t2); |
|
211 yield wait(t2, "show"); |
|
212 |
|
213 assert.ok(!isCollapsed(t2, w1) && !isCollapsed(t2, w3), |
|
214 "toolbar#2 is not collapsed"); |
|
215 |
|
216 yield close(w3); |
|
217 |
|
218 assert.ok(isAttached(t2, w1), "still attached to last window"); |
|
219 assert.ok(!isCollapsed(t2, w1), "still isn't collapsed"); |
|
220 |
|
221 t2.destroy(); |
|
222 yield wait(t2, "detach"); |
|
223 |
|
224 assert.ok(!isAttached(t2, w1), "toolbar was removed"); |
|
225 }; |
|
226 |
|
227 exports["test toolbar persistence"] = function*(assert) { |
|
228 const t1 = new Toolbar({ title: "per sist ence" }); |
|
229 |
|
230 yield wait(t1, "attach"); |
|
231 |
|
232 assert.equal(t1.hidden, false, "toolbar is visible"); |
|
233 |
|
234 hide(t1); |
|
235 yield wait(t1, "hide"); |
|
236 |
|
237 assert.equal(t1.hidden, true, "toolbar is hidden"); |
|
238 assert.ok(isCollapsed(t1), "toolbar is collapsed"); |
|
239 |
|
240 t1.destroy(); |
|
241 |
|
242 yield wait(t1, "detach"); |
|
243 |
|
244 const t2 = new Toolbar({ title: "per sist ence" }); |
|
245 |
|
246 yield wait(t2, "attach"); |
|
247 |
|
248 assert.equal(t2.hidden, true, "toolbar persisted state"); |
|
249 assert.ok(isCollapsed(t2), "toolbar is collapsed"); |
|
250 |
|
251 show(t2); |
|
252 t2.destroy(); |
|
253 |
|
254 yield wait(t2, "detach"); |
|
255 |
|
256 const t3 = new Toolbar({ title: "per sist ence", hidden: true }); |
|
257 |
|
258 yield wait(t3, "attach"); |
|
259 |
|
260 assert.equal(t3.hidden, false, "toolbar persisted state & ignored option"); |
|
261 assert.ok(!isCollapsed(t3), "toolbar isn1t collapsed"); |
|
262 |
|
263 t3.destroy(); |
|
264 |
|
265 yield wait(t3, "detach"); |
|
266 }; |
|
267 |
|
268 |
|
269 exports["test toolbar unload"] = function*(assert) { |
|
270 // We override add-on id, otherwise two instances of Toolbar host (view.js) |
|
271 // handling same updates, cause message port is bound to add-on id. |
|
272 const loader = Loader(module, null, null, {id: "toolbar-unload-addon"}); |
|
273 const { Toolbar } = loader.require("sdk/ui/toolbar"); |
|
274 |
|
275 const w1 = getMostRecentBrowserWindow(); |
|
276 const w2 = open(); |
|
277 |
|
278 yield ready(w2); |
|
279 |
|
280 const t1 = new Toolbar({ title: "unload" }); |
|
281 |
|
282 yield wait(t1, "attach"); |
|
283 |
|
284 assert.ok(isAttached(t1, w1) && isAttached(t1, w2), |
|
285 "attached to both windows"); |
|
286 |
|
287 |
|
288 loader.unload(); |
|
289 |
|
290 |
|
291 assert.ok(!isAttached(t1, w1) && !isAttached(t1, w2), |
|
292 "detached from both windows on unload"); |
|
293 |
|
294 yield close(w2); |
|
295 }; |
|
296 |
|
297 exports["test toolbar close button"] = function*(assert) { |
|
298 const t1 = new Toolbar({ title: "close with button" }); |
|
299 |
|
300 yield wait(t1, "attach"); |
|
301 const w1 = getMostRecentBrowserWindow(); |
|
302 const w2 = open(); |
|
303 |
|
304 yield ready(w2); |
|
305 |
|
306 assert.ok(!isCollapsed(t1, w1) && !isCollapsed(t1, w2), |
|
307 "toolbar isn't collapsed"); |
|
308 |
|
309 closeViaButton(t1); |
|
310 |
|
311 yield wait(t1, "hide"); |
|
312 |
|
313 assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2), |
|
314 "toolbar was collapsed"); |
|
315 |
|
316 t1.destroy(); |
|
317 yield wait(t1, "detach"); |
|
318 yield close(w2); |
|
319 }; |
|
320 |
|
321 exports["test title change"] = function*(assert) { |
|
322 const w1 = getMostRecentBrowserWindow(); |
|
323 const w2 = open(); |
|
324 |
|
325 yield ready(w2); |
|
326 |
|
327 const t1 = new Toolbar({ title: "first title" }); |
|
328 const id = t1.id; |
|
329 |
|
330 yield wait(t1, "attach"); |
|
331 |
|
332 |
|
333 assert.equal(t1.title, "first title", |
|
334 "correct title is set"); |
|
335 assert.equal(readTitle(t1, w1), "first title", |
|
336 "title set in the view of first window"); |
|
337 assert.equal(readTitle(t1, w2), "first title", |
|
338 "title set in the view of second window"); |
|
339 |
|
340 retitle(t1, "second title"); |
|
341 |
|
342 // Hide & show so to make sure changes go through a round |
|
343 // loop. |
|
344 hide(t1); |
|
345 yield wait(t1, "hide"); |
|
346 show(t1); |
|
347 yield wait(t1, "show"); |
|
348 |
|
349 assert.equal(t1.id, id, "id remains same"); |
|
350 assert.equal(t1.title, "second title", "instance title was updated"); |
|
351 assert.equal(readTitle(t1, w1), "second title", |
|
352 "title updated in first window"); |
|
353 assert.equal(readTitle(t1, w2), "second title", |
|
354 "title updated in second window"); |
|
355 |
|
356 t1.destroy(); |
|
357 yield wait(t1, "detach"); |
|
358 yield close(w2); |
|
359 }; |
|
360 |
|
361 exports["test toolbar is not customizable"] = function*(assert, done) { |
|
362 const { window, document, gCustomizeMode } = getMostRecentBrowserWindow(); |
|
363 const outerId = getOuterId(window); |
|
364 const input = new CustomizationInput(); |
|
365 const customized = defer(); |
|
366 const customizedEnd = defer(); |
|
367 |
|
368 new Reactor({ onStep: value => { |
|
369 if (value[outerId] === true) |
|
370 customized.resolve(); |
|
371 if (value[outerId] === null) |
|
372 customizedEnd.resolve(); |
|
373 }}).run(input); |
|
374 |
|
375 const toolbar = new Toolbar({ title: "foo" }); |
|
376 |
|
377 yield wait(toolbar, "attach"); |
|
378 |
|
379 let view = document.getElementById(toolbar.id); |
|
380 let label = view.querySelector("label"); |
|
381 let inner = view.querySelector("toolbar"); |
|
382 |
|
383 assert.equal(view.getAttribute("customizable"), "false", |
|
384 "The outer toolbar is not customizable."); |
|
385 |
|
386 assert.ok(label.collapsed, |
|
387 "The label is not displayed.") |
|
388 |
|
389 assert.equal(inner.getAttribute("customizable"), "true", |
|
390 "The inner toolbar is customizable."); |
|
391 |
|
392 assert.equal(window.getComputedStyle(inner).visibility, "visible", |
|
393 "The inner toolbar is visible."); |
|
394 |
|
395 // Enter in customization mode |
|
396 gCustomizeMode.toggle(); |
|
397 |
|
398 yield customized.promise; |
|
399 |
|
400 assert.equal(view.getAttribute("customizable"), "false", |
|
401 "The outer toolbar is not customizable."); |
|
402 |
|
403 assert.equal(label.collapsed, false, |
|
404 "The label is displayed.") |
|
405 |
|
406 assert.equal(inner.getAttribute("customizable"), "true", |
|
407 "The inner toolbar is customizable."); |
|
408 |
|
409 assert.equal(window.getComputedStyle(inner).visibility, "hidden", |
|
410 "The inner toolbar is hidden."); |
|
411 |
|
412 // Exit from customization mode |
|
413 gCustomizeMode.toggle(); |
|
414 |
|
415 yield customizedEnd.promise; |
|
416 |
|
417 assert.equal(view.getAttribute("customizable"), "false", |
|
418 "The outer toolbar is not customizable."); |
|
419 |
|
420 assert.ok(label.collapsed, |
|
421 "The label is not displayed.") |
|
422 |
|
423 assert.equal(inner.getAttribute("customizable"), "true", |
|
424 "The inner toolbar is customizable."); |
|
425 |
|
426 assert.equal(window.getComputedStyle(inner).visibility, "visible", |
|
427 "The inner toolbar is visible."); |
|
428 |
|
429 toolbar.destroy(); |
|
430 }; |
|
431 |
|
432 exports["test button are attached to toolbar"] = function*(assert) { |
|
433 const { document } = getMostRecentBrowserWindow(); |
|
434 const { ActionButton, ToggleButton } = require("sdk/ui"); |
|
435 const { identify } = require("sdk/ui/id"); |
|
436 |
|
437 let action = ActionButton({ |
|
438 id: "btn-1", |
|
439 label: "action", |
|
440 icon: "./placeholder.png" |
|
441 }); |
|
442 |
|
443 let toggle = ToggleButton({ |
|
444 id: "btn-2", |
|
445 label: "toggle", |
|
446 icon: "./placeholder.png" |
|
447 }); |
|
448 |
|
449 const toolbar = new Toolbar({ |
|
450 title: "foo", |
|
451 items: [action, toggle] |
|
452 }); |
|
453 |
|
454 yield wait(toolbar, "attach"); |
|
455 |
|
456 let actionNode = document.getElementById(identify(action)); |
|
457 let toggleNode = document.getElementById(identify(toggle)); |
|
458 |
|
459 assert.notEqual(actionNode, null, |
|
460 "action button exists in the document"); |
|
461 |
|
462 assert.notEqual(actionNode, null, |
|
463 "action button exists in the document"); |
|
464 |
|
465 assert.notEqual(toggleNode, null, |
|
466 "toggle button exists in the document"); |
|
467 |
|
468 assert.equal(actionNode.nextElementSibling, toggleNode, |
|
469 "action button is placed before toggle button"); |
|
470 |
|
471 assert.equal(actionNode.parentNode.parentNode.id, toolbar.id, |
|
472 "buttons are placed in the correct toolbar"); |
|
473 |
|
474 toolbar.destroy(); |
|
475 }; |
|
476 |
|
477 require("sdk/test").run(exports); |