Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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';
6 module.metadata = {
7 'engines': {
8 'Firefox': '> 28'
9 }
10 };
12 const { Cu } = require('chrome');
13 const { Loader } = require('sdk/test/loader');
14 const { data } = require('sdk/self');
15 const { open, focus, close } = require('sdk/window/helpers');
16 const { setTimeout } = require('sdk/timers');
17 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
18 const { partial } = require('sdk/lang/functional');
20 const openBrowserWindow = partial(open, null, {features: {toolbar: true}});
21 const openPrivateBrowserWindow = partial(open, null,
22 {features: {toolbar: true, private: true}});
24 function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
25 const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
26 const { AREA_NAVBAR } = CustomizableUI;
28 let widgets = CustomizableUI.getWidgetIdsInArea(AREA_NAVBAR).
29 filter((id) => id.startsWith('action-button--') && id.endsWith(buttonId));
31 if (widgets.length === 0)
32 throw new Error('Widget with id `' + id +'` not found.');
34 if (widgets.length > 1)
35 throw new Error('Unexpected number of widgets: ' + widgets.length)
37 return CustomizableUI.getWidget(widgets[0]).forWindow(window);
38 };
40 exports['test basic constructor validation'] = function(assert) {
41 let loader = Loader(module);
42 let { ActionButton } = loader.require('sdk/ui');
44 assert.throws(
45 () => ActionButton({}),
46 /^The option/,
47 'throws on no option given');
49 // Test no label
50 assert.throws(
51 () => ActionButton({ id: 'my-button', icon: './icon.png'}),
52 /^The option "label"/,
53 'throws on no label given');
55 // Test no id
56 assert.throws(
57 () => ActionButton({ label: 'my button', icon: './icon.png' }),
58 /^The option "id"/,
59 'throws on no id given');
61 // Test no icon
62 assert.throws(
63 () => ActionButton({ id: 'my-button', label: 'my button' }),
64 /^The option "icon"/,
65 'throws on no icon given');
68 // Test empty label
69 assert.throws(
70 () => ActionButton({ id: 'my-button', label: '', icon: './icon.png' }),
71 /^The option "label"/,
72 'throws on no valid label given');
74 // Test invalid id
75 assert.throws(
76 () => ActionButton({ id: 'my button', label: 'my button', icon: './icon.png' }),
77 /^The option "id"/,
78 'throws on no valid id given');
80 // Test empty id
81 assert.throws(
82 () => ActionButton({ id: '', label: 'my button', icon: './icon.png' }),
83 /^The option "id"/,
84 'throws on no valid id given');
86 // Test remote icon
87 assert.throws(
88 () => ActionButton({ id: 'my-button', label: 'my button', icon: 'http://www.mozilla.org/favicon.ico'}),
89 /^The option "icon"/,
90 'throws on no valid icon given');
92 // Test wrong icon: no absolute URI to local resource, neither relative './'
93 assert.throws(
94 () => ActionButton({ id: 'my-button', label: 'my button', icon: 'icon.png'}),
95 /^The option "icon"/,
96 'throws on no valid icon given');
98 // Test wrong icon: no absolute URI to local resource, neither relative './'
99 assert.throws(
100 () => ActionButton({ id: 'my-button', label: 'my button', icon: 'foo and bar'}),
101 /^The option "icon"/,
102 'throws on no valid icon given');
104 // Test wrong icon: '../' is not allowed
105 assert.throws(
106 () => ActionButton({ id: 'my-button', label: 'my button', icon: '../icon.png'}),
107 /^The option "icon"/,
108 'throws on no valid icon given');
110 loader.unload();
111 };
113 exports['test button added'] = function(assert) {
114 let loader = Loader(module);
115 let { ActionButton } = loader.require('sdk/ui');
117 let button = ActionButton({
118 id: 'my-button-1',
119 label: 'my button',
120 icon: './icon.png'
121 });
123 // check defaults
124 assert.equal(button.disabled, false,
125 'disabled is set to default `false` value');
127 let { node } = getWidget(button.id);
129 assert.ok(!!node, 'The button is in the navbar');
131 assert.equal(button.label, node.getAttribute('label'),
132 'label is set');
134 assert.equal(button.label, node.getAttribute('tooltiptext'),
135 'tooltip is set');
137 assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'),
138 'icon is set');
140 loader.unload();
141 }
143 exports['test button added with resource URI'] = function(assert) {
144 let loader = Loader(module);
145 let { ActionButton } = loader.require('sdk/ui');
147 let button = ActionButton({
148 id: 'my-button-1',
149 label: 'my button',
150 icon: data.url('icon.png')
151 });
153 assert.equal(button.icon, data.url('icon.png'),
154 'icon is set');
156 let { node } = getWidget(button.id);
158 assert.equal(button.icon, node.getAttribute('image'),
159 'icon on node is set');
161 loader.unload();
162 }
164 exports['test button duplicate id'] = function(assert) {
165 let loader = Loader(module);
166 let { ActionButton } = loader.require('sdk/ui');
168 let button = ActionButton({
169 id: 'my-button-2',
170 label: 'my button',
171 icon: './icon.png'
172 });
174 assert.throws(() => {
175 let doppelganger = ActionButton({
176 id: 'my-button-2',
177 label: 'my button',
178 icon: './icon.png'
179 });
180 },
181 /^The ID/,
182 'No duplicates allowed');
184 loader.unload();
185 }
187 exports['test button multiple destroy'] = function(assert) {
188 let loader = Loader(module);
189 let { ActionButton } = loader.require('sdk/ui');
191 let button = ActionButton({
192 id: 'my-button-2',
193 label: 'my button',
194 icon: './icon.png'
195 });
197 button.destroy();
198 button.destroy();
199 button.destroy();
201 assert.pass('multiple destroy doesn\'t matter');
203 loader.unload();
204 }
206 exports['test button removed on dispose'] = function(assert, done) {
207 const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
208 let loader = Loader(module);
209 let { ActionButton } = loader.require('sdk/ui');
211 let widgetId;
213 CustomizableUI.addListener({
214 onWidgetDestroyed: function(id) {
215 if (id === widgetId) {
216 CustomizableUI.removeListener(this);
218 assert.pass('button properly removed');
219 loader.unload();
220 done();
221 }
222 }
223 });
225 let button = ActionButton({
226 id: 'my-button-3',
227 label: 'my button',
228 icon: './icon.png'
229 });
231 // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
232 // was removed or it's not in the UX build yet
233 widgetId = getWidget(button.id).id;
235 button.destroy();
236 };
238 exports['test button global state updated'] = function(assert) {
239 let loader = Loader(module);
240 let { ActionButton } = loader.require('sdk/ui');
242 let button = ActionButton({
243 id: 'my-button-4',
244 label: 'my button',
245 icon: './icon.png'
246 });
248 // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
249 // was removed or it's not in the UX build yet
251 let { node, id: widgetId } = getWidget(button.id);
253 // check read-only properties
255 assert.throws(() => button.id = 'another-id',
256 /^setting a property that has only a getter/,
257 'id cannot be set at runtime');
259 assert.equal(button.id, 'my-button-4',
260 'id is unchanged');
261 assert.equal(node.id, widgetId,
262 'node id is unchanged');
264 // check writable properties
266 button.label = 'New label';
267 assert.equal(button.label, 'New label',
268 'label is updated');
269 assert.equal(node.getAttribute('label'), 'New label',
270 'node label is updated');
271 assert.equal(node.getAttribute('tooltiptext'), 'New label',
272 'node tooltip is updated');
274 button.icon = './new-icon.png';
275 assert.equal(button.icon, './new-icon.png',
276 'icon is updated');
277 assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
278 'node image is updated');
280 button.disabled = true;
281 assert.equal(button.disabled, true,
282 'disabled is updated');
283 assert.equal(node.getAttribute('disabled'), 'true',
284 'node disabled is updated');
286 // TODO: test validation on update
288 loader.unload();
289 }
291 exports['test button global state set and get with state method'] = function(assert) {
292 let loader = Loader(module);
293 let { ActionButton } = loader.require('sdk/ui');
295 let button = ActionButton({
296 id: 'my-button-16',
297 label: 'my button',
298 icon: './icon.png'
299 });
301 // read the button's state
302 let state = button.state(button);
304 assert.equal(state.label, 'my button',
305 'label is correct');
306 assert.equal(state.icon, './icon.png',
307 'icon is correct');
308 assert.equal(state.disabled, false,
309 'disabled is correct');
311 // set the new button's state
312 button.state(button, {
313 label: 'New label',
314 icon: './new-icon.png',
315 disabled: true
316 });
318 assert.equal(button.label, 'New label',
319 'label is updated');
320 assert.equal(button.icon, './new-icon.png',
321 'icon is updated');
322 assert.equal(button.disabled, true,
323 'disabled is updated');
325 loader.unload();
326 }
328 exports['test button global state updated on multiple windows'] = function(assert, done) {
329 let loader = Loader(module);
330 let { ActionButton } = loader.require('sdk/ui');
332 let button = ActionButton({
333 id: 'my-button-5',
334 label: 'my button',
335 icon: './icon.png'
336 });
338 let nodes = [getWidget(button.id).node];
340 openBrowserWindow().then(window => {
341 nodes.push(getWidget(button.id, window).node);
343 button.label = 'New label';
344 button.icon = './new-icon.png';
345 button.disabled = true;
347 for (let node of nodes) {
348 assert.equal(node.getAttribute('label'), 'New label',
349 'node label is updated');
350 assert.equal(node.getAttribute('tooltiptext'), 'New label',
351 'node tooltip is updated');
353 assert.equal(button.icon, './new-icon.png',
354 'icon is updated');
355 assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
356 'node image is updated');
358 assert.equal(button.disabled, true,
359 'disabled is updated');
360 assert.equal(node.getAttribute('disabled'), 'true',
361 'node disabled is updated');
362 };
364 return window;
365 }).
366 then(close).
367 then(loader.unload).
368 then(done, assert.fail);
369 };
371 exports['test button window state'] = function(assert, done) {
372 let loader = Loader(module);
373 let { ActionButton } = loader.require('sdk/ui');
374 let { browserWindows } = loader.require('sdk/windows');
376 let button = ActionButton({
377 id: 'my-button-6',
378 label: 'my button',
379 icon: './icon.png'
380 });
382 let mainWindow = browserWindows.activeWindow;
383 let nodes = [getWidget(button.id).node];
385 openBrowserWindow().then(focus).then(window => {
386 nodes.push(getWidget(button.id, window).node);
388 let { activeWindow } = browserWindows;
390 button.state(activeWindow, {
391 label: 'New label',
392 icon: './new-icon.png',
393 disabled: true
394 });
396 // check the states
398 assert.equal(button.label, 'my button',
399 'global label unchanged');
400 assert.equal(button.icon, './icon.png',
401 'global icon unchanged');
402 assert.equal(button.disabled, false,
403 'global disabled unchanged');
405 let state = button.state(mainWindow);
407 assert.equal(state.label, 'my button',
408 'previous window label unchanged');
409 assert.equal(state.icon, './icon.png',
410 'previous window icon unchanged');
411 assert.equal(state.disabled, false,
412 'previous window disabled unchanged');
414 let state = button.state(activeWindow);
416 assert.equal(state.label, 'New label',
417 'active window label updated');
418 assert.equal(state.icon, './new-icon.png',
419 'active window icon updated');
420 assert.equal(state.disabled, true,
421 'active disabled updated');
423 // change the global state, only the windows without a state are affected
425 button.label = 'A good label';
427 assert.equal(button.label, 'A good label',
428 'global label updated');
429 assert.equal(button.state(mainWindow).label, 'A good label',
430 'previous window label updated');
431 assert.equal(button.state(activeWindow).label, 'New label',
432 'active window label unchanged');
434 // delete the window state will inherits the global state again
436 button.state(activeWindow, null);
438 assert.equal(button.state(activeWindow).label, 'A good label',
439 'active window label inherited');
441 // check the nodes properties
442 let node = nodes[0];
443 let state = button.state(mainWindow);
445 assert.equal(node.getAttribute('label'), state.label,
446 'node label is correct');
447 assert.equal(node.getAttribute('tooltiptext'), state.label,
448 'node tooltip is correct');
450 assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
451 'node image is correct');
452 assert.equal(node.hasAttribute('disabled'), state.disabled,
453 'disabled is correct');
455 let node = nodes[1];
456 let state = button.state(activeWindow);
458 assert.equal(node.getAttribute('label'), state.label,
459 'node label is correct');
460 assert.equal(node.getAttribute('tooltiptext'), state.label,
461 'node tooltip is correct');
463 assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
464 'node image is correct');
465 assert.equal(node.hasAttribute('disabled'), state.disabled,
466 'disabled is correct');
468 return window;
469 }).
470 then(close).
471 then(loader.unload).
472 then(done, assert.fail);
473 };
476 exports['test button tab state'] = function(assert, done) {
477 let loader = Loader(module);
478 let { ActionButton } = loader.require('sdk/ui');
479 let { browserWindows } = loader.require('sdk/windows');
480 let tabs = loader.require('sdk/tabs');
482 let button = ActionButton({
483 id: 'my-button-7',
484 label: 'my button',
485 icon: './icon.png'
486 });
488 let mainTab = tabs.activeTab;
489 let node = getWidget(button.id).node;
491 tabs.open({
492 url: 'about:blank',
493 onActivate: function onActivate(tab) {
494 tab.removeListener('activate', onActivate);
496 let { activeWindow } = browserWindows;
497 // set window state
498 button.state(activeWindow, {
499 label: 'Window label',
500 icon: './window-icon.png'
501 });
503 // set previous active tab state
504 button.state(mainTab, {
505 label: 'Tab label',
506 icon: './tab-icon.png',
507 });
509 // set current active tab state
510 button.state(tab, {
511 icon: './another-tab-icon.png',
512 disabled: true
513 });
515 // check the states
517 Cu.schedulePreciseGC(() => {
518 assert.equal(button.label, 'my button',
519 'global label unchanged');
520 assert.equal(button.icon, './icon.png',
521 'global icon unchanged');
522 assert.equal(button.disabled, false,
523 'global disabled unchanged');
525 let state = button.state(mainTab);
527 assert.equal(state.label, 'Tab label',
528 'previous tab label updated');
529 assert.equal(state.icon, './tab-icon.png',
530 'previous tab icon updated');
531 assert.equal(state.disabled, false,
532 'previous tab disabled unchanged');
534 let state = button.state(tab);
536 assert.equal(state.label, 'Window label',
537 'active tab inherited from window state');
538 assert.equal(state.icon, './another-tab-icon.png',
539 'active tab icon updated');
540 assert.equal(state.disabled, true,
541 'active disabled updated');
543 // change the global state
544 button.icon = './good-icon.png';
546 // delete the tab state
547 button.state(tab, null);
549 assert.equal(button.icon, './good-icon.png',
550 'global icon updated');
551 assert.equal(button.state(mainTab).icon, './tab-icon.png',
552 'previous tab icon unchanged');
553 assert.equal(button.state(tab).icon, './window-icon.png',
554 'tab icon inherited from window');
556 // delete the window state
557 button.state(activeWindow, null);
559 assert.equal(button.state(tab).icon, './good-icon.png',
560 'tab icon inherited from global');
562 // check the node properties
564 let state = button.state(tabs.activeTab);
566 assert.equal(node.getAttribute('label'), state.label,
567 'node label is correct');
568 assert.equal(node.getAttribute('tooltiptext'), state.label,
569 'node tooltip is correct');
570 assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
571 'node image is correct');
572 assert.equal(node.hasAttribute('disabled'), state.disabled,
573 'disabled is correct');
575 tabs.once('activate', () => {
576 // This is made in order to avoid to check the node before it
577 // is updated, need a better check
578 setTimeout(() => {
579 let state = button.state(mainTab);
581 assert.equal(node.getAttribute('label'), state.label,
582 'node label is correct');
583 assert.equal(node.getAttribute('tooltiptext'), state.label,
584 'node tooltip is correct');
585 assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
586 'node image is correct');
587 assert.equal(node.hasAttribute('disabled'), state.disabled,
588 'disabled is correct');
590 tab.close(() => {
591 loader.unload();
592 done();
593 });
594 }, 500);
595 });
597 mainTab.activate();
598 });
599 }
600 });
602 };
604 exports['test button click'] = function(assert, done) {
605 let loader = Loader(module);
606 let { ActionButton } = loader.require('sdk/ui');
607 let { browserWindows } = loader.require('sdk/windows');
609 let labels = [];
611 let button = ActionButton({
612 id: 'my-button-8',
613 label: 'my button',
614 icon: './icon.png',
615 onClick: ({label}) => labels.push(label)
616 });
618 let mainWindow = browserWindows.activeWindow;
619 let chromeWindow = getMostRecentBrowserWindow();
621 openBrowserWindow().then(focus).then(window => {
622 button.state(mainWindow, { label: 'nothing' });
623 button.state(mainWindow.tabs.activeTab, { label: 'foo'})
624 button.state(browserWindows.activeWindow, { label: 'bar' });
626 button.click();
628 focus(chromeWindow).then(() => {
629 button.click();
631 assert.deepEqual(labels, ['bar', 'foo'],
632 'button click works');
634 close(window).
635 then(loader.unload).
636 then(done, assert.fail);
637 });
638 }).then(null, assert.fail);
640 }
642 exports['test button icon set'] = function(assert) {
643 const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
644 let loader = Loader(module);
645 let { ActionButton } = loader.require('sdk/ui');
647 // Test remote icon set
648 assert.throws(
649 () => ActionButton({
650 id: 'my-button-10',
651 label: 'my button',
652 icon: {
653 '16': 'http://www.mozilla.org/favicon.ico'
654 }
655 }),
656 /^The option "icon"/,
657 'throws on no valid icon given');
659 let button = ActionButton({
660 id: 'my-button-11',
661 label: 'my button',
662 icon: {
663 '5': './icon5.png',
664 '16': './icon16.png',
665 '32': './icon32.png',
666 '64': './icon64.png'
667 }
668 });
670 let { node, id: widgetId } = getWidget(button.id);
671 let { devicePixelRatio } = node.ownerDocument.defaultView;
673 let size = 16 * devicePixelRatio;
675 assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
676 'the icon is set properly in navbar');
678 let size = 32 * devicePixelRatio;
680 CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
682 assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
683 'the icon is set properly in panel');
685 // Using `loader.unload` without move back the button to the original area
686 // raises an error in the CustomizableUI. This is doesn't happen if the
687 // button is moved manually from navbar to panel. I believe it has to do
688 // with `addWidgetToArea` method, because even with a `timeout` the issue
689 // persist.
690 CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
692 loader.unload();
693 }
695 exports['test button icon se with only one option'] = function(assert) {
696 const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
697 let loader = Loader(module);
698 let { ActionButton } = loader.require('sdk/ui');
700 // Test remote icon set
701 assert.throws(
702 () => ActionButton({
703 id: 'my-button-10',
704 label: 'my button',
705 icon: {
706 '16': 'http://www.mozilla.org/favicon.ico'
707 }
708 }),
709 /^The option "icon"/,
710 'throws on no valid icon given');
712 let button = ActionButton({
713 id: 'my-button-11',
714 label: 'my button',
715 icon: {
716 '5': './icon5.png'
717 }
718 });
720 let { node, id: widgetId } = getWidget(button.id);
722 assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
723 'the icon is set properly in navbar');
725 CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
727 assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
728 'the icon is set properly in panel');
730 // Using `loader.unload` without move back the button to the original area
731 // raises an error in the CustomizableUI. This is doesn't happen if the
732 // button is moved manually from navbar to panel. I believe it has to do
733 // with `addWidgetToArea` method, because even with a `timeout` the issue
734 // persist.
735 CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
737 loader.unload();
738 }
740 exports['test button state validation'] = function(assert) {
741 let loader = Loader(module);
742 let { ActionButton } = loader.require('sdk/ui');
743 let { browserWindows } = loader.require('sdk/windows');
745 let button = ActionButton({
746 id: 'my-button-12',
747 label: 'my button',
748 icon: './icon.png'
749 })
751 let state = button.state(button);
753 assert.throws(
754 () => button.state(button, { icon: 'http://www.mozilla.org/favicon.ico' }),
755 /^The option "icon"/,
756 'throws on remote icon given');
758 loader.unload();
759 };
761 exports['test button are not in private windows'] = function(assert, done) {
762 let loader = Loader(module);
763 let { ActionButton } = loader.require('sdk/ui');
764 let{ isPrivate } = loader.require('sdk/private-browsing');
765 let { browserWindows } = loader.require('sdk/windows');
767 let button = ActionButton({
768 id: 'my-button-13',
769 label: 'my button',
770 icon: './icon.png'
771 });
773 openPrivateBrowserWindow().then(window => {
774 assert.ok(isPrivate(window),
775 'the new window is private');
777 let { node } = getWidget(button.id, window);
779 assert.ok(!node || node.style.display === 'none',
780 'the button is not added / is not visible on private window');
782 return window;
783 }).
784 then(close).
785 then(loader.unload).
786 then(done, assert.fail)
787 }
789 exports['test button state are snapshot'] = function(assert) {
790 let loader = Loader(module);
791 let { ActionButton } = loader.require('sdk/ui');
792 let { browserWindows } = loader.require('sdk/windows');
793 let tabs = loader.require('sdk/tabs');
795 let button = ActionButton({
796 id: 'my-button-14',
797 label: 'my button',
798 icon: './icon.png'
799 });
801 let state = button.state(button);
802 let windowState = button.state(browserWindows.activeWindow);
803 let tabState = button.state(tabs.activeTab);
805 assert.deepEqual(windowState, state,
806 'window state has the same properties of button state');
808 assert.deepEqual(tabState, state,
809 'tab state has the same properties of button state');
811 assert.notEqual(windowState, state,
812 'window state is not the same object of button state');
814 assert.notEqual(tabState, state,
815 'tab state is not the same object of button state');
817 assert.deepEqual(button.state(button), state,
818 'button state has the same content of previous button state');
820 assert.deepEqual(button.state(browserWindows.activeWindow), windowState,
821 'window state has the same content of previous window state');
823 assert.deepEqual(button.state(tabs.activeTab), tabState,
824 'tab state has the same content of previous tab state');
826 assert.notEqual(button.state(button), state,
827 'button state is not the same object of previous button state');
829 assert.notEqual(button.state(browserWindows.activeWindow), windowState,
830 'window state is not the same object of previous window state');
832 assert.notEqual(button.state(tabs.activeTab), tabState,
833 'tab state is not the same object of previous tab state');
835 loader.unload();
836 }
838 exports['test button icon object is a snapshot'] = function(assert) {
839 let loader = Loader(module);
840 let { ActionButton } = loader.require('sdk/ui');
842 let icon = {
843 '16': './foo.png'
844 };
846 let button = ActionButton({
847 id: 'my-button-17',
848 label: 'my button',
849 icon: icon
850 });
852 assert.deepEqual(button.icon, icon,
853 'button.icon has the same properties of the object set in the constructor');
855 assert.notEqual(button.icon, icon,
856 'button.icon is not the same object of the object set in the constructor');
858 assert.throws(
859 () => button.icon[16] = './bar.png',
860 /16 is read-only/,
861 'properties of button.icon are ready-only'
862 );
864 let newIcon = {'16': './bar.png'};
865 button.icon = newIcon;
867 assert.deepEqual(button.icon, newIcon,
868 'button.icon has the same properties of the object set');
870 assert.notEqual(button.icon, newIcon,
871 'button.icon is not the same object of the object set');
873 loader.unload();
874 }
876 exports['test button after destroy'] = function(assert) {
877 let loader = Loader(module);
878 let { ActionButton } = loader.require('sdk/ui');
879 let { browserWindows } = loader.require('sdk/windows');
880 let { activeTab } = loader.require('sdk/tabs');
882 let button = ActionButton({
883 id: 'my-button-15',
884 label: 'my button',
885 icon: './icon.png',
886 onClick: () => assert.fail('onClick should not be called')
887 });
889 button.destroy();
891 assert.throws(
892 () => button.click(),
893 /^The state cannot be set or get/,
894 'button.click() not executed');
896 assert.throws(
897 () => button.label,
898 /^The state cannot be set or get/,
899 'button.label cannot be get after destroy');
901 assert.throws(
902 () => button.label = 'my label',
903 /^The state cannot be set or get/,
904 'button.label cannot be set after destroy');
906 assert.throws(
907 () => {
908 button.state(browserWindows.activeWindow, {
909 label: 'window label'
910 });
911 },
912 /^The state cannot be set or get/,
913 'window state label cannot be set after destroy');
915 assert.throws(
916 () => button.state(browserWindows.activeWindow).label,
917 /^The state cannot be set or get/,
918 'window state label cannot be get after destroy');
920 assert.throws(
921 () => {
922 button.state(activeTab, {
923 label: 'tab label'
924 });
925 },
926 /^The state cannot be set or get/,
927 'tab state label cannot be set after destroy');
929 assert.throws(
930 () => button.state(activeTab).label,
931 /^The state cannot be set or get/,
932 'window state label cannot se get after destroy');
934 loader.unload();
935 };
937 require('sdk/test').run(exports);