Bumping manifests a=b2g-bump
[gecko.git] / addon-sdk / source / test / test-ui-toggle-button.js
blob71a6170593bfa4329cd39fa29ca688d4132c9efa
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   }
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('toggle-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);
40 exports['test basic constructor validation'] = function(assert) {
41   let loader = Loader(module);
42   let { ToggleButton } = loader.require('sdk/ui');
44   assert.throws(
45     () => ToggleButton({}),
46     /^The option/,
47     'throws on no option given');
49   // Test no label
50   assert.throws(
51     () => ToggleButton({ id: 'my-button', icon: './icon.png'}),
52     /^The option "label"/,
53     'throws on no label given');
55   // Test no id
56   assert.throws(
57     () => ToggleButton({ label: 'my button', icon: './icon.png' }),
58     /^The option "id"/,
59     'throws on no id given');
61   // Test no icon
62   assert.throws(
63     () => ToggleButton({ id: 'my-button', label: 'my button' }),
64     /^The option "icon"/,
65     'throws on no icon given');
68   // Test empty label
69   assert.throws(
70     () => ToggleButton({ 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     () => ToggleButton({ 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     () => ToggleButton({ 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     () => ToggleButton({ 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     () => ToggleButton({ 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     () => ToggleButton({ 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     () => ToggleButton({ id: 'my-button', label: 'my button', icon: '../icon.png'}),
107     /^The option "icon"/,
108     'throws on no valid icon given');
110   // Test wrong checked
111   assert.throws(
112     () => ToggleButton({
113       id: 'my-button', label: 'my button', icon: './icon.png', checked: 'yes'}),
114     /^The option "checked"/,
115     'throws on no valid checked value given');
117   loader.unload();
120 exports['test button added'] = function(assert) {
121   let loader = Loader(module);
122   let { ToggleButton } = loader.require('sdk/ui');
124   let button = ToggleButton({
125     id: 'my-button-1',
126     label: 'my button',
127     icon: './icon.png'
128   });
130   // check defaults
131   assert.equal(button.checked, false,
132     'checked is set to default `false` value');
134   assert.equal(button.disabled, false,
135     'disabled is set to default `false` value');
137   let { node } = getWidget(button.id);
139   assert.ok(!!node, 'The button is in the navbar');
141   assert.equal(button.label, node.getAttribute('label'),
142     'label is set');
144   assert.equal(button.label, node.getAttribute('tooltiptext'),
145     'tooltip is set');
147   assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'),
148     'icon is set');
150   loader.unload();
153 exports['test button added with resource URI'] = function(assert) {
154   let loader = Loader(module);
155   let { ToggleButton } = loader.require('sdk/ui');
157   let button = ToggleButton({
158     id: 'my-button-1',
159     label: 'my button',
160     icon: data.url('icon.png')
161   });
163   assert.equal(button.icon, data.url('icon.png'),
164     'icon is set');
166   let { node } = getWidget(button.id);
168   assert.equal(button.icon, node.getAttribute('image'),
169     'icon on node is set');
171   loader.unload();
174 exports['test button duplicate id'] = function(assert) {
175   let loader = Loader(module);
176   let { ToggleButton } = loader.require('sdk/ui');
178   let button = ToggleButton({
179     id: 'my-button-2',
180     label: 'my button',
181     icon: './icon.png'
182   });
184   assert.throws(() => {
185     let doppelganger = ToggleButton({
186       id: 'my-button-2',
187       label: 'my button',
188       icon: './icon.png'
189     });
190   },
191   /^The ID/,
192   'No duplicates allowed');
194   loader.unload();
197 exports['test button multiple destroy'] = function(assert) {
198   let loader = Loader(module);
199   let { ToggleButton } = loader.require('sdk/ui');
201   let button = ToggleButton({
202     id: 'my-button-2',
203     label: 'my button',
204     icon: './icon.png'
205   });
207   button.destroy();
208   button.destroy();
209   button.destroy();
211   assert.pass('multiple destroy doesn\'t matter');
213   loader.unload();
216 exports['test button removed on dispose'] = function(assert, done) {
217   const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
218   let loader = Loader(module);
219   let { ToggleButton } = loader.require('sdk/ui');
221   let widgetId;
223   CustomizableUI.addListener({
224     onWidgetDestroyed: function(id) {
225       if (id === widgetId) {
226         CustomizableUI.removeListener(this);
228         assert.pass('button properly removed');
229         loader.unload();
230         done();
231       }
232     }
233   });
235   let button = ToggleButton({
236     id: 'my-button-3',
237     label: 'my button',
238     icon: './icon.png'
239   });
241   // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
242   // was removed or it's not in the UX build yet
243   widgetId = getWidget(button.id).id;
245   button.destroy();
248 exports['test button global state updated'] = function(assert) {
249   let loader = Loader(module);
250   let { ToggleButton } = loader.require('sdk/ui');
252   let button = ToggleButton({
253     id: 'my-button-4',
254     label: 'my button',
255     icon: './icon.png'
256   });
258   // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
259   // was removed or it's not in the UX build yet
261   let { node, id: widgetId } = getWidget(button.id);
263   // check read-only properties
265   assert.throws(() => button.id = 'another-id',
266     /^setting a property that has only a getter/,
267     'id cannot be set at runtime');
269   assert.equal(button.id, 'my-button-4',
270     'id is unchanged');
271   assert.equal(node.id, widgetId,
272     'node id is unchanged');
274   // check writable properties
276   button.label = 'New label';
277   assert.equal(button.label, 'New label',
278     'label is updated');
279   assert.equal(node.getAttribute('label'), 'New label',
280     'node label is updated');
281   assert.equal(node.getAttribute('tooltiptext'), 'New label',
282     'node tooltip is updated');
284   button.icon = './new-icon.png';
285   assert.equal(button.icon, './new-icon.png',
286     'icon is updated');
287   assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
288     'node image is updated');
290   button.disabled = true;
291   assert.equal(button.disabled, true,
292     'disabled is updated');
293   assert.equal(node.getAttribute('disabled'), 'true',
294     'node disabled is updated');
296   // TODO: test validation on update
298   loader.unload();
301 exports['test button global state set and get with state method'] = function(assert) {
302   let loader = Loader(module);
303   let { ToggleButton } = loader.require('sdk/ui');
305   let button = ToggleButton({
306     id: 'my-button-16',
307     label: 'my button',
308     icon: './icon.png'
309   });
311   // read the button's state
312   let state = button.state(button);
314   assert.equal(state.label, 'my button',
315     'label is correct');
316   assert.equal(state.icon, './icon.png',
317     'icon is correct');
318   assert.equal(state.disabled, false,
319     'disabled is correct');
321   // set the new button's state
322   button.state(button, {
323     label: 'New label',
324     icon: './new-icon.png',
325     disabled: true
326   });
328   assert.equal(button.label, 'New label',
329     'label is updated');
330   assert.equal(button.icon, './new-icon.png',
331     'icon is updated');
332   assert.equal(button.disabled, true,
333     'disabled is updated');
335   loader.unload();
338 exports['test button global state updated on multiple windows'] = function(assert, done) {
339   let loader = Loader(module);
340   let { ToggleButton } = loader.require('sdk/ui');
342   let button = ToggleButton({
343     id: 'my-button-5',
344     label: 'my button',
345     icon: './icon.png'
346   });
348   let nodes = [getWidget(button.id).node];
350   openBrowserWindow().then(window => {
351     nodes.push(getWidget(button.id, window).node);
353     button.label = 'New label';
354     button.icon = './new-icon.png';
355     button.disabled = true;
357     for (let node of nodes) {
358       assert.equal(node.getAttribute('label'), 'New label',
359         'node label is updated');
360       assert.equal(node.getAttribute('tooltiptext'), 'New label',
361         'node tooltip is updated');
363       assert.equal(button.icon, './new-icon.png',
364         'icon is updated');
365       assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
366         'node image is updated');
368       assert.equal(button.disabled, true,
369         'disabled is updated');
370       assert.equal(node.getAttribute('disabled'), 'true',
371         'node disabled is updated');
372     };
374     return window;
375   }).
376   then(close).
377   then(loader.unload).
378   then(done, assert.fail);
381 exports['test button window state'] = function(assert, done) {
382   let loader = Loader(module);
383   let { ToggleButton } = loader.require('sdk/ui');
384   let { browserWindows } = loader.require('sdk/windows');
386   let button = ToggleButton({
387     id: 'my-button-6',
388     label: 'my button',
389     icon: './icon.png'
390   });
392   let mainWindow = browserWindows.activeWindow;
393   let nodes = [getWidget(button.id).node];
395   openBrowserWindow().then(focus).then(window => {
396     nodes.push(getWidget(button.id, window).node);
398     let { activeWindow } = browserWindows;
400     button.state(activeWindow, {
401       label: 'New label',
402       icon: './new-icon.png',
403       disabled: true
404     });
406     // check the states
408     assert.equal(button.label, 'my button',
409       'global label unchanged');
410     assert.equal(button.icon, './icon.png',
411       'global icon unchanged');
412     assert.equal(button.disabled, false,
413       'global disabled unchanged');
415     let state = button.state(mainWindow);
417     assert.equal(state.label, 'my button',
418       'previous window label unchanged');
419     assert.equal(state.icon, './icon.png',
420       'previous window icon unchanged');
421     assert.equal(state.disabled, false,
422       'previous window disabled unchanged');
424     let state = button.state(activeWindow);
426     assert.equal(state.label, 'New label',
427       'active window label updated');
428     assert.equal(state.icon, './new-icon.png',
429       'active window icon updated');
430     assert.equal(state.disabled, true,
431       'active disabled updated');
433     // change the global state, only the windows without a state are affected
435     button.label = 'A good label';
437     assert.equal(button.label, 'A good label',
438       'global label updated');
439     assert.equal(button.state(mainWindow).label, 'A good label',
440       'previous window label updated');
441     assert.equal(button.state(activeWindow).label, 'New label',
442       'active window label unchanged');
444     // delete the window state will inherits the global state again
446     button.state(activeWindow, null);
448     assert.equal(button.state(activeWindow).label, 'A good label',
449       'active window label inherited');
451     // check the nodes properties
452     let node = nodes[0];
453     let state = button.state(mainWindow);
455     assert.equal(node.getAttribute('label'), state.label,
456       'node label is correct');
457     assert.equal(node.getAttribute('tooltiptext'), state.label,
458       'node tooltip is correct');
460     assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
461       'node image is correct');
462     assert.equal(node.hasAttribute('disabled'), state.disabled,
463       'disabled is correct');
465     let node = nodes[1];
466     let state = button.state(activeWindow);
468     assert.equal(node.getAttribute('label'), state.label,
469       'node label is correct');
470     assert.equal(node.getAttribute('tooltiptext'), state.label,
471       'node tooltip is correct');
473     assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
474       'node image is correct');
475     assert.equal(node.hasAttribute('disabled'), state.disabled,
476       'disabled is correct');
478     return window;
479   }).
480   then(close).
481   then(loader.unload).
482   then(done, assert.fail);
486 exports['test button tab state'] = function(assert, done) {
487   let loader = Loader(module);
488   let { ToggleButton } = loader.require('sdk/ui');
489   let { browserWindows } = loader.require('sdk/windows');
490   let tabs = loader.require('sdk/tabs');
492   let button = ToggleButton({
493     id: 'my-button-7',
494     label: 'my button',
495     icon: './icon.png'
496   });
498   let mainTab = tabs.activeTab;
499   let node = getWidget(button.id).node;
501   tabs.open({
502     url: 'about:blank',
503     onActivate: function onActivate(tab) {
504       tab.removeListener('activate', onActivate);
506       let { activeWindow } = browserWindows;
507       // set window state
508       button.state(activeWindow, {
509         label: 'Window label',
510         icon: './window-icon.png'
511       });
513       // set previous active tab state
514       button.state(mainTab, {
515         label: 'Tab label',
516         icon: './tab-icon.png',
517       });
519       // set current active tab state
520       button.state(tab, {
521         icon: './another-tab-icon.png',
522         disabled: true
523       });
525       // check the states
527       Cu.schedulePreciseGC(() => {
528         assert.equal(button.label, 'my button',
529           'global label unchanged');
530         assert.equal(button.icon, './icon.png',
531           'global icon unchanged');
532         assert.equal(button.disabled, false,
533           'global disabled unchanged');
535         let state = button.state(mainTab);
537         assert.equal(state.label, 'Tab label',
538           'previous tab label updated');
539         assert.equal(state.icon, './tab-icon.png',
540           'previous tab icon updated');
541         assert.equal(state.disabled, false,
542           'previous tab disabled unchanged');
544         let state = button.state(tab);
546         assert.equal(state.label, 'Window label',
547           'active tab inherited from window state');
548         assert.equal(state.icon, './another-tab-icon.png',
549           'active tab icon updated');
550         assert.equal(state.disabled, true,
551           'active disabled updated');
553         // change the global state
554         button.icon = './good-icon.png';
556         // delete the tab state
557         button.state(tab, null);
559         assert.equal(button.icon, './good-icon.png',
560           'global icon updated');
561         assert.equal(button.state(mainTab).icon, './tab-icon.png',
562           'previous tab icon unchanged');
563         assert.equal(button.state(tab).icon, './window-icon.png',
564           'tab icon inherited from window');
566         // delete the window state
567         button.state(activeWindow, null);
569         assert.equal(button.state(tab).icon, './good-icon.png',
570           'tab icon inherited from global');
572         // check the node properties
574         let state = button.state(tabs.activeTab);
576         assert.equal(node.getAttribute('label'), state.label,
577           'node label is correct');
578         assert.equal(node.getAttribute('tooltiptext'), state.label,
579           'node tooltip is correct');
580         assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
581           'node image is correct');
582         assert.equal(node.hasAttribute('disabled'), state.disabled,
583           'disabled is correct');
585         tabs.once('activate', () => {
586           // This is made in order to avoid to check the node before it
587           // is updated, need a better check
588           setTimeout(() => {
589             let state = button.state(mainTab);
591             assert.equal(node.getAttribute('label'), state.label,
592               'node label is correct');
593             assert.equal(node.getAttribute('tooltiptext'), state.label,
594               'node tooltip is correct');
595             assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
596               'node image is correct');
597             assert.equal(node.hasAttribute('disabled'), state.disabled,
598               'disabled is correct');
600             tab.close(() => {
601               loader.unload();
602               done();
603             });
604           }, 500);
605         });
607         mainTab.activate();
608       });
609     }
610   });
614 exports['test button click'] = function(assert, done) {
615   let loader = Loader(module);
616   let { ToggleButton } = loader.require('sdk/ui');
617   let { browserWindows } = loader.require('sdk/windows');
619   let labels = [];
621   let button = ToggleButton({
622     id: 'my-button-8',
623     label: 'my button',
624     icon: './icon.png',
625     onClick: ({label}) => labels.push(label)
626   });
628   let mainWindow = browserWindows.activeWindow;
629   let chromeWindow = getMostRecentBrowserWindow();
631   openBrowserWindow().then(focus).then(window => {
632     button.state(mainWindow, { label: 'nothing' });
633     button.state(mainWindow.tabs.activeTab, { label: 'foo'})
634     button.state(browserWindows.activeWindow, { label: 'bar' });
636     button.click();
638     focus(chromeWindow).then(() => {
639       button.click();
641       assert.deepEqual(labels, ['bar', 'foo'],
642         'button click works');
644       close(window).
645         then(loader.unload).
646         then(done, assert.fail);
647     });
648   }).then(null, assert.fail);
651 exports['test button icon set'] = function(assert) {
652   const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
653   let loader = Loader(module);
654   let { ToggleButton } = loader.require('sdk/ui');
656   // Test remote icon set
657   assert.throws(
658     () => ToggleButton({
659       id: 'my-button-10',
660       label: 'my button',
661       icon: {
662         '16': 'http://www.mozilla.org/favicon.ico'
663       }
664     }),
665     /^The option "icon"/,
666     'throws on no valid icon given');
668   let button = ToggleButton({
669     id: 'my-button-11',
670     label: 'my button',
671     icon: {
672       '5': './icon5.png',
673       '16': './icon16.png',
674       '32': './icon32.png',
675       '64': './icon64.png'
676     }
677   });
679   let { node, id: widgetId } = getWidget(button.id);
680   let { devicePixelRatio } = node.ownerDocument.defaultView;
682   let size = 16 * devicePixelRatio;
684   assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
685     'the icon is set properly in navbar');
687   let size = 32 * devicePixelRatio;
689   CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
691   assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
692     'the icon is set properly in panel');
694   // Using `loader.unload` without move back the button to the original area
695   // raises an error in the CustomizableUI. This is doesn't happen if the
696   // button is moved manually from navbar to panel. I believe it has to do
697   // with `addWidgetToArea` method, because even with a `timeout` the issue
698   // persist.
699   CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
701   loader.unload();
704 exports['test button icon se with only one option'] = function(assert) {
705   const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
706   let loader = Loader(module);
707   let { ToggleButton } = loader.require('sdk/ui');
709   // Test remote icon set
710   assert.throws(
711     () => ToggleButton({
712       id: 'my-button-10',
713       label: 'my button',
714       icon: {
715         '16': 'http://www.mozilla.org/favicon.ico'
716       }
717     }),
718     /^The option "icon"/,
719     'throws on no valid icon given');
721   let button = ToggleButton({
722     id: 'my-button-11',
723     label: 'my button',
724     icon: {
725       '5': './icon5.png'
726     }
727   });
729   let { node, id: widgetId } = getWidget(button.id);
731   assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
732     'the icon is set properly in navbar');
734   CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
736   assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
737     'the icon is set properly in panel');
739   // Using `loader.unload` without move back the button to the original area
740   // raises an error in the CustomizableUI. This is doesn't happen if the
741   // button is moved manually from navbar to panel. I believe it has to do
742   // with `addWidgetToArea` method, because even with a `timeout` the issue
743   // persist.
744   CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
746   loader.unload();
749 exports['test button state validation'] = function(assert) {
750   let loader = Loader(module);
751   let { ToggleButton } = loader.require('sdk/ui');
752   let { browserWindows } = loader.require('sdk/windows');
754   let button = ToggleButton({
755     id: 'my-button-12',
756     label: 'my button',
757     icon: './icon.png'
758   })
760   let state = button.state(button);
762   assert.throws(
763     () => button.state(button, { icon: 'http://www.mozilla.org/favicon.ico' }),
764     /^The option "icon"/,
765     'throws on remote icon given');
767   loader.unload();
770 exports['test button are not in private windows'] = function(assert, done) {
771   let loader = Loader(module);
772   let { ToggleButton } = loader.require('sdk/ui');
773   let{ isPrivate } = loader.require('sdk/private-browsing');
774   let { browserWindows } = loader.require('sdk/windows');
776   let button = ToggleButton({
777     id: 'my-button-13',
778     label: 'my button',
779     icon: './icon.png'
780   });
782   openPrivateBrowserWindow().then(window => {
783     assert.ok(isPrivate(window),
784       'the new window is private');
786     let { node } = getWidget(button.id, window);
788     assert.ok(!node || node.style.display === 'none',
789       'the button is not added / is not visible on private window');
791     return window;
792   }).
793   then(close).
794   then(loader.unload).
795   then(done, assert.fail)
798 exports['test button state are snapshot'] = function(assert) {
799   let loader = Loader(module);
800   let { ToggleButton } = loader.require('sdk/ui');
801   let { browserWindows } = loader.require('sdk/windows');
802   let tabs = loader.require('sdk/tabs');
804   let button = ToggleButton({
805     id: 'my-button-14',
806     label: 'my button',
807     icon: './icon.png'
808   });
810   let state = button.state(button);
811   let windowState = button.state(browserWindows.activeWindow);
812   let tabState = button.state(tabs.activeTab);
814   assert.deepEqual(windowState, state,
815     'window state has the same properties of button state');
817   assert.deepEqual(tabState, state,
818     'tab state has the same properties of button state');
820   assert.notEqual(windowState, state,
821     'window state is not the same object of button state');
823   assert.notEqual(tabState, state,
824     'tab state is not the same object of button state');
826   assert.deepEqual(button.state(button), state,
827     'button state has the same content of previous button state');
829   assert.deepEqual(button.state(browserWindows.activeWindow), windowState,
830     'window state has the same content of previous window state');
832   assert.deepEqual(button.state(tabs.activeTab), tabState,
833     'tab state has the same content of previous tab state');
835   assert.notEqual(button.state(button), state,
836     'button state is not the same object of previous button state');
838   assert.notEqual(button.state(browserWindows.activeWindow), windowState,
839     'window state is not the same object of previous window state');
841   assert.notEqual(button.state(tabs.activeTab), tabState,
842     'tab state is not the same object of previous tab state');
844   loader.unload();
847 exports['test button icon object is a snapshot'] = function(assert) {
848   let loader = Loader(module);
849   let { ToggleButton } = loader.require('sdk/ui');
851   let icon = {
852     '16': './foo.png'
853   };
855   let button = ToggleButton({
856     id: 'my-button-17',
857     label: 'my button',
858     icon: icon
859   });
861   assert.deepEqual(button.icon, icon,
862     'button.icon has the same properties of the object set in the constructor');
864   assert.notEqual(button.icon, icon,
865     'button.icon is not the same object of the object set in the constructor');
867   assert.throws(
868     () => button.icon[16] = './bar.png',
869     /16 is read-only/,
870     'properties of button.icon are ready-only'
871   );
873   let newIcon = {'16': './bar.png'};
874   button.icon = newIcon;
876   assert.deepEqual(button.icon, newIcon,
877     'button.icon has the same properties of the object set');
879   assert.notEqual(button.icon, newIcon,
880     'button.icon is not the same object of the object set');
882   loader.unload();
885 exports['test button after destroy'] = function(assert) {
886   let loader = Loader(module);
887   let { ToggleButton } = loader.require('sdk/ui');
888   let { browserWindows } = loader.require('sdk/windows');
889   let { activeTab } = loader.require('sdk/tabs');
891   let button = ToggleButton({
892     id: 'my-button-15',
893     label: 'my button',
894     icon: './icon.png',
895     onClick: () => assert.fail('onClick should not be called')
896   });
898   button.destroy();
900   assert.throws(
901     () => button.click(),
902     /^The state cannot be set or get/,
903     'button.click() not executed');
905   assert.throws(
906     () => button.label,
907     /^The state cannot be set or get/,
908     'button.label cannot be get after destroy');
910   assert.throws(
911     () => button.label = 'my label',
912     /^The state cannot be set or get/,
913     'button.label cannot be set after destroy');
915   assert.throws(
916     () => {
917       button.state(browserWindows.activeWindow, {
918         label: 'window label'
919       });
920     },
921     /^The state cannot be set or get/,
922     'window state label cannot be set after destroy');
924   assert.throws(
925     () => button.state(browserWindows.activeWindow).label,
926     /^The state cannot be set or get/,
927     'window state label cannot be get after destroy');
929   assert.throws(
930     () => {
931       button.state(activeTab, {
932         label: 'tab label'
933       });
934     },
935     /^The state cannot be set or get/,
936     'tab state label cannot be set after destroy');
938   assert.throws(
939     () => button.state(activeTab).label,
940     /^The state cannot be set or get/,
941     'window state label cannot se get after destroy');
943   loader.unload();
946 exports['test button checked'] = function(assert, done) {
947   let loader = Loader(module);
948   let { ToggleButton } = loader.require('sdk/ui');
949   let { browserWindows } = loader.require('sdk/windows');
951   let events = [];
953   let button = ToggleButton({
954     id: 'my-button-9',
955     label: 'my button',
956     icon: './icon.png',
957     checked: true,
958     onClick: ({label}) => events.push('clicked:' + label),
959     onChange: state => events.push('changed:' + state.label + ':' + state.checked)
960   });
962   let { node } = getWidget(button.id);
964   assert.equal(node.getAttribute('type'), 'checkbox',
965     'node type is properly set');
967   let mainWindow = browserWindows.activeWindow;
968   let chromeWindow = getMostRecentBrowserWindow();
970   openBrowserWindow().then(focus).then(window => {
971     button.state(mainWindow, { label: 'nothing' });
972     button.state(mainWindow.tabs.activeTab, { label: 'foo'})
973     button.state(browserWindows.activeWindow, { label: 'bar' });
975     button.click();
976     button.click();
978     focus(chromeWindow).then(() => {
979       button.click();
980       button.click();
982       assert.deepEqual(events, [
983           'clicked:bar', 'changed:bar:false', 'clicked:bar', 'changed:bar:true',
984           'clicked:foo', 'changed:foo:false', 'clicked:foo', 'changed:foo:true'
985         ],
986         'button change events works');
988       close(window).
989         then(loader.unload).
990         then(done, assert.fail);
991     })
992   }).then(null, assert.fail);
995 exports['test button is checked on window level'] = function(assert, done) {
996   let loader = Loader(module);
997   let { ToggleButton } = loader.require('sdk/ui');
998   let { browserWindows } = loader.require('sdk/windows');
999   let tabs = loader.require('sdk/tabs');
1001   let button = ToggleButton({
1002     id: 'my-button-20',
1003     label: 'my button',
1004     icon: './icon.png'
1005   });
1007   let mainWindow = browserWindows.activeWindow;
1008   let mainTab = tabs.activeTab;
1010   assert.equal(button.checked, false,
1011     'global state, checked is `false`.');
1012   assert.equal(button.state(mainTab).checked, false,
1013     'tab state, checked is `false`.');
1014   assert.equal(button.state(mainWindow).checked, false,
1015     'window state, checked is `false`.');
1017   button.click();
1019   tabs.open({
1020     url: 'about:blank',
1021     onActivate: function onActivate(tab) {
1022       tab.removeListener('activate', onActivate);
1024       assert.notEqual(mainTab, tab,
1025         'the current tab is not the same.');
1027       assert.equal(button.checked, false,
1028         'global state, checked is `false`.');
1029       assert.equal(button.state(mainTab).checked, true,
1030         'previous tab state, checked is `true`.');
1031       assert.equal(button.state(tab).checked, true,
1032         'current tab state, checked is `true`.');
1033       assert.equal(button.state(mainWindow).checked, true,
1034         'window state, checked is `true`.');
1036       openBrowserWindow().then(focus).then(window => {
1037         let { activeWindow } = browserWindows;
1038         let { activeTab } = activeWindow.tabs;
1040         assert.equal(button.checked, false,
1041           'global state, checked is `false`.');
1042         assert.equal(button.state(activeTab).checked, false,
1043           'tab state, checked is `false`.');
1045         assert.equal(button.state(activeWindow).checked, false,
1046           'window state, checked is `false`.');
1048         tab.close(()=> {
1049           close(window).
1050             then(loader.unload).
1051             then(done, assert.fail);
1052         })
1053       }).
1054       then(null, assert.fail);
1055     }
1056   });
1060 exports['test button click do not messing up states'] = function(assert) {
1061   let loader = Loader(module);
1062   let { ToggleButton } = loader.require('sdk/ui');
1063   let { browserWindows } = loader.require('sdk/windows');
1065   let button = ToggleButton({
1066     id: 'my-button-21',
1067     label: 'my button',
1068     icon: './icon.png'
1069   });
1071   let mainWindow = browserWindows.activeWindow;
1072   let { activeTab } = mainWindow.tabs;
1074   button.state(mainWindow, { icon: './new-icon.png' });
1075   button.state(activeTab, { label: 'foo'})
1077   assert.equal(button.state(mainWindow).label, 'my button',
1078     'label property for window state, properly derived from global state');
1080   assert.equal(button.state(activeTab).icon, './new-icon.png',
1081     'icon property for tab state, properly derived from window state');
1083   button.click();
1085   button.label = 'bar';
1087   assert.equal(button.state(mainWindow).label, 'bar',
1088     'label property for window state, properly derived from global state');
1090   button.state(mainWindow, null);
1092   assert.equal(button.state(activeTab).icon, './icon.png',
1093     'icon property for tab state, properly derived from window state');
1095   loader.unload();
1098 exports['test buttons can have anchored panels'] = function(assert, done) {
1099   let loader = Loader(module);
1100   let { ToggleButton } = loader.require('sdk/ui');
1101   let { Panel } = loader.require('sdk/panel');
1102   let { identify } = loader.require('sdk/ui/id');
1103   let { getActiveView } = loader.require('sdk/view/core');
1105   let b1 = ToggleButton({
1106     id: 'my-button-22',
1107     label: 'my button',
1108     icon: './icon.png',
1109     onChange: ({checked}) => checked && panel.show()
1110   });
1112   let b2 = ToggleButton({
1113     id: 'my-button-23',
1114     label: 'my button',
1115     icon: './icon.png',
1116     onChange: ({checked}) => checked && panel.show({position: b2})
1117   });
1119   let panel = Panel({
1120     position: b1
1121   });
1123   let { document } = getMostRecentBrowserWindow();
1124   let b1Node = document.getElementById(identify(b1));
1125   let b2Node = document.getElementById(identify(b2));
1126   let panelNode = getActiveView(panel);
1128   panel.once('show', () => {
1129     assert.ok(b1.state('window').checked,
1130       'button is checked');
1132     assert.equal(panelNode.getAttribute('type'), 'arrow',
1133       'the panel is a arrow type');
1135     assert.strictEqual(b1Node, panelNode.anchorNode,
1136       'the panel is anchored properly to the button given in costructor');
1138     panel.hide();
1140     panel.once('show', () => {
1141       assert.ok(b2.state('window').checked,
1142         'button is checked');
1144       assert.equal(panelNode.getAttribute('type'), 'arrow',
1145         'the panel is a arrow type');
1147       // test also that the button passed in `show` method, takes the precedence
1148       // over the button set in panel's constructor.
1149       assert.strictEqual(b2Node, panelNode.anchorNode,
1150         'the panel is anchored properly to the button passed to show method');
1152       loader.unload();
1154       done();
1155     });
1157     b2.click();
1158   });
1160   b1.click();
1163 require('sdk/test').run(exports);