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