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
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"engines": {
"Firefox": "*"
}
};
const { Toolbar } = require("sdk/ui/toolbar");
const { Loader } = require("sdk/test/loader");
const { identify } = require("sdk/ui/id");
const { getMostRecentBrowserWindow, open, getOuterId } = require("sdk/window/utils");
const { ready, close } = require("sdk/window/helpers");
const { defer } = require("sdk/core/promise");
const { send, stop, Reactor } = require("sdk/event/utils");
const { object } = require("sdk/util/sequence");
const { CustomizationInput } = require("sdk/input/customizable-ui");
const { OutputPort } = require("sdk/output/system");
const output = new OutputPort({ id: "toolbar-change" });
const { cleanUI } = require('sdk/test/utils');
const tabs = require("sdk/tabs");
const wait = (toolbar, event) => {
let { promise, resolve } = defer();
toolbar.once(event, resolve);
return promise;
};
const show = ({id}) => send(output, object([id, {collapsed: false}]));
const hide = ({id}) => send(output, object([id, {collapsed: true}]));
const retitle = ({id}, title) => send(output, object([id, {title: title}]));
const isAttached = ({id}, window=getMostRecentBrowserWindow()) =>
!!window.document.getElementById(id);
const isCollapsed = ({id}, window=getMostRecentBrowserWindow()) =>
window.document.getElementById(id).getAttribute("collapsed") === "true";
const closeViaButton = ({id}, window=getMostRecentBrowserWindow()) =>
window.document.getElementById("close-" + id).click();
const readTitle = ({id}, window=getMostRecentBrowserWindow()) =>
window.document.getElementById(id).getAttribute("toolbarname");
exports["test toolbar API"] = function*(assert) {
assert.throws(() => new Toolbar(),
/The `option.title`/,
"toolbar requires title");
assert.throws(() => new Toolbar({ hidden: false }),
/The `option.title`/,
"toolbar requires title");
const t1 = new Toolbar({ title: "foo" });
assert.throws(() => new Toolbar({ title: "foo" }),
/already exists/,
"can't create identical toolbars");
assert.ok(t1.id, "toolbar has an id");
assert.equal(t1.id, identify(t1), "identify returns toolbar id");
assert.deepEqual(t1.items, [], "toolbar items are empty");
assert.equal(t1.title, void(0), "title is void until attached");
assert.equal(t1.hidden, void(0), "hidden is void until attached");
yield wait(t1, "attach");
assert.equal(t1.title, "foo", "title is set after attach");
assert.equal(t1.hidden, false, "by default toolbar isn't hidden");
assert.throws(() => new Toolbar({ title: "foo" }),
/already exists/,
"still can't create identical toolbar");
const t2 = new Toolbar({ title: "bar", hidden: true });
assert.pass("can create different toolbar though");
assert.ok(t2.id, "toolbar has an id");
assert.equal(t2.id, identify(t2), "identify returns toolbar id");
assert.notEqual(t2.id, t1.id, "each toolbar has unique id");
yield wait(t2, "attach");
assert.equal(t2.title, "bar", "title is set after attach");
assert.equal(t2.hidden, true, "toolbar is hidden as specified");
t2.destroy();
t1.destroy();
yield wait(t1, "detach");
assert.equal(t1.title, void(0), "title is voided after detach");
assert.equal(t1.hidden, void(0), "hidden is void fater detach");
const t3 = new Toolbar({ title: "foo" });
assert.pass("Can create toolbar after identical was detached");
assert.equal(t3.id, t1.id, "toolbar has a same id");
assert.equal(t3.id, identify(t3), "identify returns toolbar.id");
assert.equal(t3.title, void(0), "title is void before attach");
assert.equal(t3.hidden, void(0), "hidden is void before attach");
yield wait(t3, "attach");
assert.equal(t3.title, "foo", "title is set");
assert.equal(t3.hidden, false, "toolbar is hidden");
t3.destroy();
yield wait(t3, "detach");
};
exports["test show / hide toolbar"] = function*(assert) {
const t1 = new Toolbar({ title: "foo" });
yield wait(t1, "attach");
assert.equal(t1.title, "foo", "title is set after attach");
assert.equal(t1.hidden, false, "by default toolbar isn't hidden");
assert.ok(isAttached(t1), "toolbar was actually attarched");
assert.ok(!isCollapsed(t1), "toolbar isn't collapsed");
hide(t1);
assert.equal(t1.hidden, false, "not hidden yet");
yield wait(t1, "hide");
assert.equal(t1.hidden, true, "toolbar got hidden");
assert.ok(isCollapsed(t1), "toolbar is collapsed");
show(t1);
yield wait(t1, "show");
assert.equal(t1.hidden, false, "toolbar got shown");
assert.ok(!isCollapsed(t1), "toolbar isn't collapsed");
t1.destroy();
yield wait(t1, "detach");
assert.ok(!isAttached(t1), "toolbar is no longer attached");
};
exports["test multiple windows & toolbars"] = function*(assert) {
const w1 = getMostRecentBrowserWindow();
const t1 = new Toolbar({ title: "multi window" });
yield wait(t1, "attach");
assert.equal(t1.title, "multi window", "title is set after attach");
assert.equal(t1.hidden, false, "by default toolbar isn't hidden");
assert.ok(isAttached(t1, w1), "toolbar was actually attarched");
assert.ok(!isCollapsed(t1, w1), "toolbar isn't collapsed");
const w2 = open();
yield ready(w2);
assert.ok(isAttached(t1, w2), "toolbar was attached to second window");
assert.ok(!isCollapsed(t1, w2), "toolbar isn't collabsed");
hide(t1);
yield wait(t1, "hide");
assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2),
"toolbar is collabsed");
const w3 = open();
yield ready(w3);
assert.ok(isAttached(t1, w1) && isAttached(t1, w2) && isAttached(t1, w3),
"toolbar is attached to all windows");
assert.ok(isCollapsed(t1, w3) && isCollapsed(t1, w3) && isCollapsed(t1, w3),
"toolbar still collapsed in all windows");
const t2 = new Toolbar({ title: "multi hidden", hidden: true });
yield wait(t2, "attach");
assert.equal(t2.title, "multi hidden", "title is set after attach");
assert.equal(t2.hidden, true, "isn't hidden as specified");
assert.ok(isAttached(t1, w1) && isAttached(t1, w2) && isAttached(t1, w3),
"toolbar#1 is still attached");
assert.ok(isAttached(t2, w1) && isAttached(t2, w2) && isAttached(t2, w3),
"toolbar#2 was attached to all windows");
assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2) && isCollapsed(t1, w3),
"toolbar#1 is still collapsed");
assert.ok(isCollapsed(t2, w1) && isCollapsed(t2, w2) && isCollapsed(t2, w3),
"toolbar#2 is collapsed");
t1.destroy();
yield wait(t1, "detach");
assert.ok(!isAttached(t1, w1) && !isAttached(t1, w2) && !isAttached(t1, w3),
"toolbar#1 was detached from all windows");
assert.ok(isAttached(t2, w1) && isAttached(t2, w2) && isAttached(t2, w3),
"toolbar#2 is still attached to all windows");
yield close(w2);
assert.ok(isAttached(t2, w1) && isAttached(t2, w3),
"toolbar#2 is still attached to remaining windows");
assert.ok(isCollapsed(t2, w1) && isCollapsed(t2, w3),
"toolbar#2 is still collapsed");
show(t2);
yield wait(t2, "show");
assert.ok(!isCollapsed(t2, w1) && !isCollapsed(t2, w3),
"toolbar#2 is not collapsed");
yield close(w3);
assert.ok(isAttached(t2, w1), "still attached to last window");
assert.ok(!isCollapsed(t2, w1), "still isn't collapsed");
t2.destroy();
yield wait(t2, "detach");
assert.ok(!isAttached(t2, w1), "toolbar was removed");
yield cleanUI();
};
exports["test toolbar persistence"] = function*(assert) {
const t1 = new Toolbar({ title: "per sist ence" });
yield wait(t1, "attach");
assert.equal(t1.hidden, false, "toolbar is visible");
hide(t1);
yield wait(t1, "hide");
assert.equal(t1.hidden, true, "toolbar is hidden");
assert.ok(isCollapsed(t1), "toolbar is collapsed");
t1.destroy();
yield wait(t1, "detach");
const t2 = new Toolbar({ title: "per sist ence" });
yield wait(t2, "attach");
assert.equal(t2.hidden, true, "toolbar persisted state");
assert.ok(isCollapsed(t2), "toolbar is collapsed");
show(t2);
t2.destroy();
yield wait(t2, "detach");
const t3 = new Toolbar({ title: "per sist ence", hidden: true });
yield wait(t3, "attach");
assert.equal(t3.hidden, false, "toolbar persisted state & ignored option");
assert.ok(!isCollapsed(t3), "toolbar isn1t collapsed");
t3.destroy();
yield wait(t3, "detach");
yield cleanUI();
};
exports["test toolbar unload"] = function*(assert) {
// We override add-on id, otherwise two instances of Toolbar host (view.js)
// handling same updates, cause message port is bound to add-on id.
const loader = Loader(module, null, null, {id: "toolbar-unload-addon"});
const { Toolbar } = loader.require("sdk/ui/toolbar");
const w1 = getMostRecentBrowserWindow();
const w2 = open();
yield ready(w2);
const t1 = new Toolbar({ title: "unload" });
yield wait(t1, "attach");
assert.ok(isAttached(t1, w1) && isAttached(t1, w2),
"attached to both windows");
loader.unload();
assert.ok(!isAttached(t1, w1) && !isAttached(t1, w2),
"detached from both windows on unload");
yield cleanUI();
};
exports["test toolbar close button"] = function*(assert) {
const t1 = new Toolbar({ title: "close with button" });
yield wait(t1, "attach");
const w1 = getMostRecentBrowserWindow();
const w2 = open();
yield ready(w2);
assert.ok(!isCollapsed(t1, w1) && !isCollapsed(t1, w2),
"toolbar isn't collapsed");
closeViaButton(t1);
yield wait(t1, "hide");
assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2),
"toolbar was collapsed");
t1.destroy();
yield wait(t1, "detach");
yield cleanUI();
};
exports["test title change"] = function*(assert) {
const w1 = getMostRecentBrowserWindow();
const w2 = open();
yield ready(w2);
const t1 = new Toolbar({ title: "first title" });
const id = t1.id;
yield wait(t1, "attach");
assert.equal(t1.title, "first title",
"correct title is set");
assert.equal(readTitle(t1, w1), "first title",
"title set in the view of first window");
assert.equal(readTitle(t1, w2), "first title",
"title set in the view of second window");
retitle(t1, "second title");
// Hide & show so to make sure changes go through a round
// loop.
hide(t1);
yield wait(t1, "hide");
show(t1);
yield wait(t1, "show");
assert.equal(t1.id, id, "id remains same");
assert.equal(t1.title, "second title", "instance title was updated");
assert.equal(readTitle(t1, w1), "second title",
"title updated in first window");
assert.equal(readTitle(t1, w2), "second title",
"title updated in second window");
t1.destroy();
yield wait(t1, "detach");
yield cleanUI();
};
exports["test toolbar is not customizable"] = function*(assert, done) {
const { window, document, gCustomizeMode } = getMostRecentBrowserWindow();
const outerId = getOuterId(window);
const input = new CustomizationInput();
const customized = defer();
const customizedEnd = defer();
// open a new tab so that the customize tab replaces it
// and does not replace the start tab.
yield new Promise(resolve => {
tabs.open({
url: "about:blank",
onReady: resolve
});
});
new Reactor({ onStep: value => {
if (value[outerId] === true)
customized.resolve();
if (value[outerId] === null)
customizedEnd.resolve();
}}).run(input);
const toolbar = new Toolbar({ title: "foo" });
yield wait(toolbar, "attach");
let view = document.getElementById(toolbar.id);
let label = view.querySelector("label");
let inner = view.querySelector("toolbar");
assert.equal(view.getAttribute("customizable"), "false",
"The outer toolbar is not customizable.");
assert.ok(label.collapsed,
"The label is not displayed.")
assert.equal(inner.getAttribute("customizable"), "true",
"The inner toolbar is customizable.");
assert.equal(window.getComputedStyle(inner).visibility, "visible",
"The inner toolbar is visible.");
// Enter in customization mode
gCustomizeMode.toggle();
yield customized.promise;
assert.equal(view.getAttribute("customizable"), "false",
"The outer toolbar is not customizable.");
assert.equal(label.collapsed, false,
"The label is displayed.")
assert.equal(inner.getAttribute("customizable"), "true",
"The inner toolbar is customizable.");
assert.equal(window.getComputedStyle(inner).visibility, "hidden",
"The inner toolbar is hidden.");
// Exit from customization mode
gCustomizeMode.toggle();
yield customizedEnd.promise;
assert.equal(view.getAttribute("customizable"), "false",
"The outer toolbar is not customizable.");
assert.ok(label.collapsed,
"The label is not displayed.")
assert.equal(inner.getAttribute("customizable"), "true",
"The inner toolbar is customizable.");
assert.equal(window.getComputedStyle(inner).visibility, "visible",
"The inner toolbar is visible.");
toolbar.destroy();
yield cleanUI();
};
exports["test button are attached to toolbar"] = function*(assert) {
const { document } = getMostRecentBrowserWindow();
const { ActionButton, ToggleButton } = require("sdk/ui");
const { identify } = require("sdk/ui/id");
let action = ActionButton({
id: "btn-1",
label: "action",
icon: "./placeholder.png"
});
let toggle = ToggleButton({
id: "btn-2",
label: "toggle",
icon: "./placeholder.png"
});
const toolbar = new Toolbar({
title: "foo",
items: [action, toggle]
});
yield wait(toolbar, "attach");
let actionNode = document.getElementById(identify(action));
let toggleNode = document.getElementById(identify(toggle));
assert.notEqual(actionNode, null,
"action button exists in the document");
assert.notEqual(actionNode, null,
"action button exists in the document");
assert.notEqual(toggleNode, null,
"toggle button exists in the document");
assert.equal(actionNode.nextElementSibling, toggleNode,
"action button is placed before toggle button");
assert.equal(actionNode.parentNode.parentNode.id, toolbar.id,
"buttons are placed in the correct toolbar");
toolbar.destroy();
yield cleanUI();
};
exports["test toolbar are not in private windows"] = function*(assert) {
const w = open(null, {features: {toolbar: true, private: true}});
yield ready(w);
const t = new Toolbar({title: "foo"});
yield wait(t, "attach");
assert.ok(!isAttached(t), "toolbar wasn't actually attached");
t.destroy();
yield cleanUI();
}
require("sdk/test").run(module.exports);
|