/* 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': '> 28' } }; const { Cu } = require('chrome'); const { Loader } = require('sdk/test/loader'); const { data } = require('sdk/self'); const { open, focus, close } = require('sdk/window/helpers'); const { setTimeout } = require('sdk/timers'); const { getMostRecentBrowserWindow } = require('sdk/window/utils'); const { partial } = require('sdk/lang/functional'); const { wait } = require('./event/helpers'); const { gc } = require('sdk/test/memory'); const packaging = require("@loader/options"); const openBrowserWindow = partial(open, null, {features: {toolbar: true}}); const openPrivateBrowserWindow = partial(open, null, {features: {toolbar: true, private: true}}); const badgeNodeFor = (node) => node.ownerDocument.getAnonymousElementByAttribute(node, 'class', 'toolbarbutton-badge'); function getWidget(buttonId, window = getMostRecentBrowserWindow()) { const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); const { AREA_NAVBAR } = CustomizableUI; let widgets = CustomizableUI.getWidgetIdsInArea(AREA_NAVBAR). filter((id) => id.startsWith('toggle-button--') && id.endsWith(buttonId)); if (widgets.length === 0) throw new Error('Widget with id `' + id +'` not found.'); if (widgets.length > 1) throw new Error('Unexpected number of widgets: ' + widgets.length) return CustomizableUI.getWidget(widgets[0]).forWindow(window); }; exports['test basic constructor validation'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); assert.throws( () => ToggleButton({}), /^The option/, 'throws on no option given'); // Test no label assert.throws( () => ToggleButton({ id: 'my-button', icon: './icon.png'}), /^The option "label"/, 'throws on no label given'); // Test no id assert.throws( () => ToggleButton({ label: 'my button', icon: './icon.png' }), /^The option "id"/, 'throws on no id given'); // Test no icon assert.throws( () => ToggleButton({ id: 'my-button', label: 'my button' }), /^The option "icon"/, 'throws on no icon given'); // Test empty label assert.throws( () => ToggleButton({ id: 'my-button', label: '', icon: './icon.png' }), /^The option "label"/, 'throws on no valid label given'); // Test invalid id assert.throws( () => ToggleButton({ id: 'my button', label: 'my button', icon: './icon.png' }), /^The option "id"/, 'throws on no valid id given'); // Test empty id assert.throws( () => ToggleButton({ id: '', label: 'my button', icon: './icon.png' }), /^The option "id"/, 'throws on no valid id given'); // Test remote icon assert.throws( () => ToggleButton({ id: 'my-button', label: 'my button', icon: 'http://www.mozilla.org/favicon.ico'}), /^The option "icon"/, 'throws on no valid icon given'); // Test wrong icon: no absolute URI to local resource, neither relative './' assert.throws( () => ToggleButton({ id: 'my-button', label: 'my button', icon: 'icon.png'}), /^The option "icon"/, 'throws on no valid icon given'); // Test wrong icon: no absolute URI to local resource, neither relative './' assert.throws( () => ToggleButton({ id: 'my-button', label: 'my button', icon: 'foo and bar'}), /^The option "icon"/, 'throws on no valid icon given'); // Test wrong icon: '../' is not allowed assert.throws( () => ToggleButton({ id: 'my-button', label: 'my button', icon: '../icon.png'}), /^The option "icon"/, 'throws on no valid icon given'); assert.throws( () => ToggleButton({ id: 'my-button', label: 'button', icon: './i.png', badge: true}), /^The option "badge"/, 'throws on no valid badge given'); assert.throws( () => ToggleButton({ id: 'my-button', label: 'button', icon: './i.png', badgeColor: true}), /^The option "badgeColor"/, 'throws on no valid badge given'); loader.unload(); }; exports['test button added'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-1', label: 'my button', icon: './icon.png' }); // check defaults assert.equal(button.disabled, false, 'disabled is set to default `false` value'); let { node } = getWidget(button.id); assert.ok(!!node, 'The button is in the navbar'); assert.equal(button.label, node.getAttribute('label'), 'label is set'); assert.equal(button.label, node.getAttribute('tooltiptext'), 'tooltip is set'); assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'), 'icon is set'); assert.equal("", node.getAttribute('badge'), 'badge attribute is empty'); loader.unload(); } exports['test button is not garbaged'] = function (assert, done) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); ToggleButton({ id: 'my-button-1', label: 'my button', icon: './icon.png', onClick: () => { loader.unload(); done(); } }); gc().then(() => { let { node } = getWidget('my-button-1'); assert.ok(!!node, 'The button is in the navbar'); assert.equal('my button', node.getAttribute('label'), 'label is set'); assert.equal(data.url('icon.png'), node.getAttribute('image'), 'icon is set'); // ensure the listener is not gc'ed too node.click(); }).catch(assert.fail); } exports['test button added with resource URI'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-1', label: 'my button', icon: data.url('icon.png') }); assert.equal(button.icon, data.url('icon.png'), 'icon is set'); let { node } = getWidget(button.id); assert.equal(button.icon, node.getAttribute('image'), 'icon on node is set'); loader.unload(); } exports['test button duplicate id'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-2', label: 'my button', icon: './icon.png' }); assert.throws(() => { let doppelganger = ToggleButton({ id: 'my-button-2', label: 'my button', icon: './icon.png' }); }, /^The ID/, 'No duplicates allowed'); loader.unload(); } exports['test button multiple destroy'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-2', label: 'my button', icon: './icon.png' }); button.destroy(); button.destroy(); button.destroy(); assert.pass('multiple destroy doesn\'t matter'); loader.unload(); } exports['test button removed on dispose'] = function(assert, done) { const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let widgetId; CustomizableUI.addListener({ onWidgetDestroyed: function(id) { if (id === widgetId) { CustomizableUI.removeListener(this); assert.pass('button properly removed'); loader.unload(); done(); } } }); let button = ToggleButton({ id: 'my-button-3', label: 'my button', icon: './icon.png' }); // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it // was removed or it's not in the UX build yet widgetId = getWidget(button.id).id; button.destroy(); }; exports['test button global state updated'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-4', label: 'my button', icon: './icon.png', }); // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it // was removed or it's not in the UX build yet let { node, id: widgetId } = getWidget(button.id); // check read-only properties assert.throws(() => button.id = 'another-id', /^setting a property that has only a getter/, 'id cannot be set at runtime'); assert.equal(button.id, 'my-button-4', 'id is unchanged'); assert.equal(node.id, widgetId, 'node id is unchanged'); // check writable properties button.label = 'New label'; assert.equal(button.label, 'New label', 'label is updated'); assert.equal(node.getAttribute('label'), 'New label', 'node label is updated'); assert.equal(node.getAttribute('tooltiptext'), 'New label', 'node tooltip is updated'); button.icon = './new-icon.png'; assert.equal(button.icon, './new-icon.png', 'icon is updated'); assert.equal(node.getAttribute('image'), data.url('new-icon.png'), 'node image is updated'); button.disabled = true; assert.equal(button.disabled, true, 'disabled is updated'); assert.equal(node.getAttribute('disabled'), 'true', 'node disabled is updated'); button.badge = '+2'; button.badgeColor = 'blue'; assert.equal(button.badge, '+2', 'badge is updated'); assert.equal(node.getAttribute('bagde'), '', 'node badge is updated'); assert.equal(button.badgeColor, 'blue', 'badgeColor is updated'); assert.equal(badgeNodeFor(node).style.backgroundColor, 'blue', 'badge color is updated'); // TODO: test validation on update loader.unload(); } exports['test button global state set and get with state method'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-16', label: 'my button', icon: './icon.png' }); // read the button's state let state = button.state(button); assert.equal(state.label, 'my button', 'label is correct'); assert.equal(state.icon, './icon.png', 'icon is correct'); assert.equal(state.disabled, false, 'disabled is correct'); // set the new button's state button.state(button, { label: 'New label', icon: './new-icon.png', disabled: true, badge: '+2', badgeColor: 'blue' }); assert.equal(button.label, 'New label', 'label is updated'); assert.equal(button.icon, './new-icon.png', 'icon is updated'); assert.equal(button.disabled, true, 'disabled is updated'); assert.equal(button.badge, '+2', 'badge is updated'); assert.equal(button.badgeColor, 'blue', 'badgeColor is updated'); loader.unload(); } exports['test button global state updated on multiple windows'] = function*(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-5', label: 'my button', icon: './icon.png' }); let nodes = [getWidget(button.id).node]; let window = yield openBrowserWindow(); nodes.push(getWidget(button.id, window).node); button.label = 'New label'; button.icon = './new-icon.png'; button.disabled = true; button.badge = '+10'; button.badgeColor = 'green'; for (let node of nodes) { assert.equal(node.getAttribute('label'), 'New label', 'node label is updated'); assert.equal(node.getAttribute('tooltiptext'), 'New label', 'node tooltip is updated'); assert.equal(button.icon, './new-icon.png', 'icon is updated'); assert.equal(node.getAttribute('image'), data.url('new-icon.png'), 'node image is updated'); assert.equal(button.disabled, true, 'disabled is updated'); assert.equal(node.getAttribute('disabled'), 'true', 'node disabled is updated'); assert.equal(button.badge, '+10', 'badge is updated') assert.equal(button.badgeColor, 'green', 'badgeColor is updated') assert.equal(node.getAttribute('badge'), '+10', 'node badge is updated') assert.equal(badgeNodeFor(node).style.backgroundColor, 'green', 'node badge color is updated') }; yield close(window); loader.unload(); }; exports['test button window state'] = function*(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let button = ToggleButton({ id: 'my-button-6', label: 'my button', icon: './icon.png', badge: '+1', badgeColor: 'red' }); let mainWindow = browserWindows.activeWindow; let nodes = [getWidget(button.id).node]; let window = yield openBrowserWindow().then(focus); nodes.push(getWidget(button.id, window).node); let { activeWindow } = browserWindows; button.state(activeWindow, { label: 'New label', icon: './new-icon.png', disabled: true, badge: '+2', badgeColor : 'green' }); // check the states assert.equal(button.label, 'my button', 'global label unchanged'); assert.equal(button.icon, './icon.png', 'global icon unchanged'); assert.equal(button.disabled, false, 'global disabled unchanged'); assert.equal(button.badge, '+1', 'global badge unchanged'); assert.equal(button.badgeColor, 'red', 'global badgeColor unchanged'); let state = button.state(mainWindow); assert.equal(state.label, 'my button', 'previous window label unchanged'); assert.equal(state.icon, './icon.png', 'previous window icon unchanged'); assert.equal(state.disabled, false, 'previous window disabled unchanged'); assert.deepEqual(button.badge, '+1', 'previouswindow badge unchanged'); assert.deepEqual(button.badgeColor, 'red', 'previous window badgeColor unchanged'); state = button.state(activeWindow); assert.equal(state.label, 'New label', 'active window label updated'); assert.equal(state.icon, './new-icon.png', 'active window icon updated'); assert.equal(state.disabled, true, 'active disabled updated'); assert.equal(state.badge, '+2', 'active badge updated'); assert.equal(state.badgeColor, 'green', 'active badgeColor updated'); // change the global state, only the windows without a state are affected button.label = 'A good label'; button.badge = '+3'; assert.equal(button.label, 'A good label', 'global label updated'); assert.equal(button.state(mainWindow).label, 'A good label', 'previous window label updated'); assert.equal(button.state(activeWindow).label, 'New label', 'active window label unchanged'); assert.equal(button.state(activeWindow).badge, '+2', 'active badge unchanged'); assert.equal(button.state(activeWindow).badgeColor, 'green', 'active badgeColor unchanged'); assert.equal(button.state(mainWindow).badge, '+3', 'previous window badge updated'); assert.equal(button.state(mainWindow).badgeColor, 'red', 'previous window badgeColor unchanged'); // delete the window state will inherits the global state again button.state(activeWindow, null); state = button.state(activeWindow); assert.equal(state.label, 'A good label', 'active window label inherited'); assert.equal(state.badge, '+3', 'previous window badge inherited'); assert.equal(button.badgeColor, 'red', 'previous window badgeColor inherited'); // check the nodes properties let node = nodes[0]; state = button.state(mainWindow); assert.equal(node.getAttribute('label'), state.label, 'node label is correct'); assert.equal(node.getAttribute('tooltiptext'), state.label, 'node tooltip is correct'); assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 'node image is correct'); assert.equal(node.hasAttribute('disabled'), state.disabled, 'disabled is correct'); assert.equal(node.getAttribute("badge"), state.badge, 'badge is correct'); assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor, 'badge color is correct'); node = nodes[1]; state = button.state(activeWindow); assert.equal(node.getAttribute('label'), state.label, 'node label is correct'); assert.equal(node.getAttribute('tooltiptext'), state.label, 'node tooltip is correct'); assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 'node image is correct'); assert.equal(node.hasAttribute('disabled'), state.disabled, 'disabled is correct'); assert.equal(node.getAttribute('badge'), state.badge, 'badge is correct'); assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor, 'badge color is correct'); yield close(window); loader.unload(); }; exports['test button tab state'] = function*(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let tabs = loader.require('sdk/tabs'); let button = ToggleButton({ id: 'my-button-7', label: 'my button', icon: './icon.png' }); let mainTab = tabs.activeTab; let node = getWidget(button.id).node; tabs.open('about:blank'); yield wait(tabs, 'ready'); let tab = tabs.activeTab; let { activeWindow } = browserWindows; // set window state button.state(activeWindow, { label: 'Window label', icon: './window-icon.png', badge: 'win', badgeColor: 'blue' }); // set previous active tab state button.state(mainTab, { label: 'Tab label', icon: './tab-icon.png', badge: 'tab', badgeColor: 'red' }); // set current active tab state button.state(tab, { icon: './another-tab-icon.png', disabled: true, badge: 't1', badgeColor: 'green' }); // check the states, be sure they won't be gc'ed yield gc(); assert.equal(button.label, 'my button', 'global label unchanged'); assert.equal(button.icon, './icon.png', 'global icon unchanged'); assert.equal(button.disabled, false, 'global disabled unchanged'); assert.equal(button.badge, undefined, 'global badge unchanged') let state = button.state(mainTab); assert.equal(state.label, 'Tab label', 'previous tab label updated'); assert.equal(state.icon, './tab-icon.png', 'previous tab icon updated'); assert.equal(state.disabled, false, 'previous tab disabled unchanged'); assert.equal(state.badge, 'tab', 'previous tab badge unchanged') assert.equal(state.badgeColor, 'red', 'previous tab badgeColor unchanged') state = button.state(tab); assert.equal(state.label, 'Window label', 'active tab inherited from window state'); assert.equal(state.icon, './another-tab-icon.png', 'active tab icon updated'); assert.equal(state.disabled, true, 'active disabled updated'); assert.equal(state.badge, 't1', 'active badge updated'); assert.equal(state.badgeColor, 'green', 'active badgeColor updated'); // change the global state button.icon = './good-icon.png'; // delete the tab state button.state(tab, null); assert.equal(button.icon, './good-icon.png', 'global icon updated'); assert.equal(button.state(mainTab).icon, './tab-icon.png', 'previous tab icon unchanged'); assert.equal(button.state(tab).icon, './window-icon.png', 'tab icon inherited from window'); assert.equal(button.state(mainTab).badge, 'tab', 'previous tab badge is unchaged'); assert.equal(button.state(tab).badge, 'win', 'tab badge is inherited from window'); // delete the window state button.state(activeWindow, null); state = button.state(tab); assert.equal(state.icon, './good-icon.png', 'tab icon inherited from global'); assert.equal(state.badge, undefined, 'tab badge inherited from global'); assert.equal(state.badgeColor, undefined, 'tab badgeColor inherited from global'); // check the node properties yield wait(); assert.equal(node.getAttribute('label'), state.label, 'node label is correct'); assert.equal(node.getAttribute('tooltiptext'), state.label, 'node tooltip is correct'); assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 'node image is correct'); assert.equal(node.hasAttribute('disabled'), state.disabled, 'node disabled is correct'); assert.equal(node.getAttribute('badge'), '', 'badge text is correct'); assert.equal(badgeNodeFor(node).style.backgroundColor, '', 'badge color is correct'); mainTab.activate(); yield wait(tabs, 'activate'); // This is made in order to avoid to check the node before it // is updated, need a better check yield wait(); state = button.state(mainTab); assert.equal(node.getAttribute('label'), state.label, 'node label is correct'); assert.equal(node.getAttribute('tooltiptext'), state.label, 'node tooltip is correct'); assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 'node image is correct'); assert.equal(node.hasAttribute('disabled'), state.disabled, 'disabled is correct'); assert.equal(node.getAttribute('badge'), state.badge, 'badge text is correct'); assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor, 'badge color is correct'); tab.close(loader.unload); loader.unload(); }; exports['test button click'] = function*(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let labels = []; let button = ToggleButton({ id: 'my-button-8', label: 'my button', icon: './icon.png', onClick: ({label}) => labels.push(label) }); let mainWindow = browserWindows.activeWindow; let chromeWindow = getMostRecentBrowserWindow(); let window = yield openBrowserWindow().then(focus); button.state(mainWindow, { label: 'nothing' }); button.state(mainWindow.tabs.activeTab, { label: 'foo'}) button.state(browserWindows.activeWindow, { label: 'bar' }); button.click(); yield focus(chromeWindow); button.click(); assert.deepEqual(labels, ['bar', 'foo'], 'button click works'); yield close(window); loader.unload(); } exports['test button icon set'] = function(assert) { const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); // Test remote icon set assert.throws( () => ToggleButton({ id: 'my-button-10', label: 'my button', icon: { '16': 'http://www.mozilla.org/favicon.ico' } }), /^The option "icon"/, 'throws on no valid icon given'); let button = ToggleButton({ id: 'my-button-11', label: 'my button', icon: { '5': './icon5.png', '16': './icon16.png', '32': './icon32.png', '64': './icon64.png' } }); let { node, id: widgetId } = getWidget(button.id); let { devicePixelRatio } = node.ownerDocument.defaultView; let size = 16 * devicePixelRatio; assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)), 'the icon is set properly in navbar'); size = 32 * devicePixelRatio; CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL); assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)), 'the icon is set properly in panel'); // Using `loader.unload` without move back the button to the original area // raises an error in the CustomizableUI. This is doesn't happen if the // button is moved manually from navbar to panel. I believe it has to do // with `addWidgetToArea` method, because even with a `timeout` the issue // persist. CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR); loader.unload(); } exports['test button icon set with only one option'] = function(assert) { const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); // Test remote icon set assert.throws( () => ToggleButton({ id: 'my-button-10', label: 'my button', icon: { '16': 'http://www.mozilla.org/favicon.ico' } }), /^The option "icon"/, 'throws on no valid icon given'); let button = ToggleButton({ id: 'my-button-11', label: 'my button', icon: { '5': './icon5.png' } }); let { node, id: widgetId } = getWidget(button.id); assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)), 'the icon is set properly in navbar'); CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL); assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)), 'the icon is set properly in panel'); // Using `loader.unload` without move back the button to the original area // raises an error in the CustomizableUI. This is doesn't happen if the // button is moved manually from navbar to panel. I believe it has to do // with `addWidgetToArea` method, because even with a `timeout` the issue // persist. CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR); loader.unload(); } exports['test button state validation'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let button = ToggleButton({ id: 'my-button-12', label: 'my button', icon: './icon.png' }) let state = button.state(button); assert.throws( () => button.state(button, { icon: 'http://www.mozilla.org/favicon.ico' }), /^The option "icon"/, 'throws on remote icon given'); assert.throws( () => button.state(button, { badge: true } ), /^The option "badge"/, 'throws on wrong badge value given'); loader.unload(); }; exports['test button are not in private windows'] = function(assert, done) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let{ isPrivate } = loader.require('sdk/private-browsing'); let { browserWindows } = loader.require('sdk/windows'); let button = ToggleButton({ id: 'my-button-13', label: 'my button', icon: './icon.png' }); openPrivateBrowserWindow().then(window => { assert.ok(isPrivate(window), 'the new window is private'); let { node } = getWidget(button.id, window); assert.ok(!node || node.style.display === 'none', 'the button is not added / is not visible on private window'); return window; }). then(close). then(loader.unload). then(done, assert.fail) } exports['test button state are snapshot'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let tabs = loader.require('sdk/tabs'); let button = ToggleButton({ id: 'my-button-14', label: 'my button', icon: './icon.png' }); let state = button.state(button); let windowState = button.state(browserWindows.activeWindow); let tabState = button.state(tabs.activeTab); assert.deepEqual(windowState, state, 'window state has the same properties of button state'); assert.deepEqual(tabState, state, 'tab state has the same properties of button state'); assert.notEqual(windowState, state, 'window state is not the same object of button state'); assert.notEqual(tabState, state, 'tab state is not the same object of button state'); assert.deepEqual(button.state(button), state, 'button state has the same content of previous button state'); assert.deepEqual(button.state(browserWindows.activeWindow), windowState, 'window state has the same content of previous window state'); assert.deepEqual(button.state(tabs.activeTab), tabState, 'tab state has the same content of previous tab state'); assert.notEqual(button.state(button), state, 'button state is not the same object of previous button state'); assert.notEqual(button.state(browserWindows.activeWindow), windowState, 'window state is not the same object of previous window state'); assert.notEqual(button.state(tabs.activeTab), tabState, 'tab state is not the same object of previous tab state'); loader.unload(); } exports['test button icon object is a snapshot'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let icon = { '16': './foo.png' }; let button = ToggleButton({ id: 'my-button-17', label: 'my button', icon: icon }); assert.deepEqual(button.icon, icon, 'button.icon has the same properties of the object set in the constructor'); assert.notEqual(button.icon, icon, 'button.icon is not the same object of the object set in the constructor'); assert.throws( () => button.icon[16] = './bar.png', /16 is read-only/, 'properties of button.icon are ready-only' ); let newIcon = {'16': './bar.png'}; button.icon = newIcon; assert.deepEqual(button.icon, newIcon, 'button.icon has the same properties of the object set'); assert.notEqual(button.icon, newIcon, 'button.icon is not the same object of the object set'); loader.unload(); } exports['test button after destroy'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let { activeTab } = loader.require('sdk/tabs'); let button = ToggleButton({ id: 'my-button-15', label: 'my button', icon: './icon.png', onClick: () => assert.fail('onClick should not be called') }); button.destroy(); assert.throws( () => button.click(), /^The state cannot be set or get/, 'button.click() not executed'); assert.throws( () => button.label, /^The state cannot be set or get/, 'button.label cannot be get after destroy'); assert.throws( () => button.label = 'my label', /^The state cannot be set or get/, 'button.label cannot be set after destroy'); assert.throws( () => { button.state(browserWindows.activeWindow, { label: 'window label' }); }, /^The state cannot be set or get/, 'window state label cannot be set after destroy'); assert.throws( () => button.state(browserWindows.activeWindow).label, /^The state cannot be set or get/, 'window state label cannot be get after destroy'); assert.throws( () => { button.state(activeTab, { label: 'tab label' }); }, /^The state cannot be set or get/, 'tab state label cannot be set after destroy'); assert.throws( () => button.state(activeTab).label, /^The state cannot be set or get/, 'window state label cannot se get after destroy'); loader.unload(); }; exports['test button badge property'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-18', label: 'my button', icon: './icon.png', badge: 123456 }); assert.equal(button.badge, 123456, 'badge is set'); assert.equal(button.badgeColor, undefined, 'badge color is not set'); let { node } = getWidget(button.id); let { getComputedStyle } = node.ownerDocument.defaultView; let badgeNode = badgeNodeFor(node); assert.equal('1234', node.getAttribute('badge'), 'badge text is displayed up to four characters'); assert.equal(getComputedStyle(badgeNode).backgroundColor, 'rgb(217, 0, 0)', 'badge color is the default one'); button.badge = '危機'; assert.equal(button.badge, '危機', 'badge is properly set'); assert.equal('危機', node.getAttribute('badge'), 'badge text is displayed'); button.badge = '🐶🐰🐹'; assert.equal(button.badge, '🐶🐰🐹', 'badge is properly set'); assert.equal('🐶🐰🐹', node.getAttribute('badge'), 'badge text is displayed'); loader.unload(); } exports['test button badge color'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let button = ToggleButton({ id: 'my-button-19', label: 'my button', icon: './icon.png', badge: '+1', badgeColor: 'blue' }); assert.equal(button.badgeColor, 'blue', 'badge color is set'); let { node } = getWidget(button.id); let { getComputedStyle } = node.ownerDocument.defaultView; let badgeNode = badgeNodeFor(node); assert.equal(badgeNodeFor(node).style.backgroundColor, 'blue', 'badge color is displayed properly'); assert.equal(getComputedStyle(badgeNode).backgroundColor, 'rgb(0, 0, 255)', 'badge color overrides the default one'); loader.unload(); } // toggle button only exports['test button checked'] = function(assert, done) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let events = []; let button = ToggleButton({ id: 'my-button-9', label: 'my button', icon: './icon.png', checked: true, onClick: ({label}) => events.push('clicked:' + label), onChange: state => events.push('changed:' + state.label + ':' + state.checked) }); let { node } = getWidget(button.id); assert.equal(node.getAttribute('type'), 'checkbox', 'node type is properly set'); let mainWindow = browserWindows.activeWindow; let chromeWindow = getMostRecentBrowserWindow(); openBrowserWindow().then(focus).then(window => { button.state(mainWindow, { label: 'nothing' }); button.state(mainWindow.tabs.activeTab, { label: 'foo'}) button.state(browserWindows.activeWindow, { label: 'bar' }); button.click(); button.click(); focus(chromeWindow).then(() => { button.click(); button.click(); assert.deepEqual(events, [ 'clicked:bar', 'changed:bar:false', 'clicked:bar', 'changed:bar:true', 'clicked:foo', 'changed:foo:false', 'clicked:foo', 'changed:foo:true' ], 'button change events works'); close(window). then(loader.unload). then(done, assert.fail); }) }).then(null, assert.fail); } exports['test button is checked on window level'] = function(assert, done) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let tabs = loader.require('sdk/tabs'); let button = ToggleButton({ id: 'my-button-20', label: 'my button', icon: './icon.png' }); let mainWindow = browserWindows.activeWindow; let mainTab = tabs.activeTab; assert.equal(button.checked, false, 'global state, checked is `false`.'); assert.equal(button.state(mainTab).checked, false, 'tab state, checked is `false`.'); assert.equal(button.state(mainWindow).checked, false, 'window state, checked is `false`.'); button.click(); tabs.open({ url: 'about:blank', onActivate: function onActivate(tab) { tab.removeListener('activate', onActivate); assert.notEqual(mainTab, tab, 'the current tab is not the same.'); assert.equal(button.checked, false, 'global state, checked is `false`.'); assert.equal(button.state(mainTab).checked, true, 'previous tab state, checked is `true`.'); assert.equal(button.state(tab).checked, true, 'current tab state, checked is `true`.'); assert.equal(button.state(mainWindow).checked, true, 'window state, checked is `true`.'); openBrowserWindow().then(focus).then(window => { let { activeWindow } = browserWindows; let { activeTab } = activeWindow.tabs; assert.equal(button.checked, false, 'global state, checked is `false`.'); assert.equal(button.state(activeTab).checked, false, 'tab state, checked is `false`.'); assert.equal(button.state(activeWindow).checked, false, 'window state, checked is `false`.'); tab.close(()=> { close(window). then(loader.unload). then(done, assert.fail); }) }). then(null, assert.fail); } }); }; exports['test button click do not messing up states'] = function(assert) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { browserWindows } = loader.require('sdk/windows'); let button = ToggleButton({ id: 'my-button-21', label: 'my button', icon: './icon.png' }); let mainWindow = browserWindows.activeWindow; let { activeTab } = mainWindow.tabs; button.state(mainWindow, { icon: './new-icon.png' }); button.state(activeTab, { label: 'foo'}) assert.equal(button.state(mainWindow).label, 'my button', 'label property for window state, properly derived from global state'); assert.equal(button.state(activeTab).icon, './new-icon.png', 'icon property for tab state, properly derived from window state'); button.click(); button.label = 'bar'; assert.equal(button.state(mainWindow).label, 'bar', 'label property for window state, properly derived from global state'); button.state(mainWindow, null); assert.equal(button.state(activeTab).icon, './icon.png', 'icon property for tab state, properly derived from window state'); loader.unload(); } exports['test buttons can have anchored panels'] = function(assert, done) { let loader = Loader(module); let { ToggleButton } = loader.require('sdk/ui'); let { Panel } = loader.require('sdk/panel'); let { identify } = loader.require('sdk/ui/id'); let { getActiveView } = loader.require('sdk/view/core'); let b1 = ToggleButton({ id: 'my-button-22', label: 'my button', icon: './icon.png', onChange: ({checked}) => checked && panel.show() }); let b2 = ToggleButton({ id: 'my-button-23', label: 'my button', icon: './icon.png', onChange: ({checked}) => checked && panel.show({position: b2}) }); let panel = Panel({ position: b1 }); let { document } = getMostRecentBrowserWindow(); let b1Node = document.getElementById(identify(b1)); let b2Node = document.getElementById(identify(b2)); let panelNode = getActiveView(panel); panel.once('show', () => { assert.ok(b1.state('window').checked, 'button is checked'); assert.equal(panelNode.getAttribute('type'), 'arrow', 'the panel is a arrow type'); assert.strictEqual(b1Node, panelNode.anchorNode, 'the panel is anchored properly to the button given in costructor'); panel.hide(); panel.once('show', () => { assert.ok(b2.state('window').checked, 'button is checked'); assert.equal(panelNode.getAttribute('type'), 'arrow', 'the panel is a arrow type'); // test also that the button passed in `show` method, takes the precedence // over the button set in panel's constructor. assert.strictEqual(b2Node, panelNode.anchorNode, 'the panel is anchored properly to the button passed to show method'); loader.unload(); done(); }); b2.click(); }); b1.click(); } if (packaging.isNative) { module.exports = { "test skip on jpm": (assert) => assert.pass("skipping this file with jpm") }; } require("sdk/test").run(module.exports);