1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-ui-toggle-button.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1163 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +'use strict'; 1.8 + 1.9 +module.metadata = { 1.10 + 'engines': { 1.11 + 'Firefox': '> 28' 1.12 + } 1.13 +}; 1.14 + 1.15 +const { Cu } = require('chrome'); 1.16 +const { Loader } = require('sdk/test/loader'); 1.17 +const { data } = require('sdk/self'); 1.18 +const { open, focus, close } = require('sdk/window/helpers'); 1.19 +const { setTimeout } = require('sdk/timers'); 1.20 +const { getMostRecentBrowserWindow } = require('sdk/window/utils'); 1.21 +const { partial } = require('sdk/lang/functional'); 1.22 + 1.23 +const openBrowserWindow = partial(open, null, {features: {toolbar: true}}); 1.24 +const openPrivateBrowserWindow = partial(open, null, 1.25 + {features: {toolbar: true, private: true}}); 1.26 + 1.27 +function getWidget(buttonId, window = getMostRecentBrowserWindow()) { 1.28 + const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); 1.29 + const { AREA_NAVBAR } = CustomizableUI; 1.30 + 1.31 + let widgets = CustomizableUI.getWidgetIdsInArea(AREA_NAVBAR). 1.32 + filter((id) => id.startsWith('toggle-button--') && id.endsWith(buttonId)); 1.33 + 1.34 + if (widgets.length === 0) 1.35 + throw new Error('Widget with id `' + id +'` not found.'); 1.36 + 1.37 + if (widgets.length > 1) 1.38 + throw new Error('Unexpected number of widgets: ' + widgets.length) 1.39 + 1.40 + return CustomizableUI.getWidget(widgets[0]).forWindow(window); 1.41 +}; 1.42 + 1.43 +exports['test basic constructor validation'] = function(assert) { 1.44 + let loader = Loader(module); 1.45 + let { ToggleButton } = loader.require('sdk/ui'); 1.46 + 1.47 + assert.throws( 1.48 + () => ToggleButton({}), 1.49 + /^The option/, 1.50 + 'throws on no option given'); 1.51 + 1.52 + // Test no label 1.53 + assert.throws( 1.54 + () => ToggleButton({ id: 'my-button', icon: './icon.png'}), 1.55 + /^The option "label"/, 1.56 + 'throws on no label given'); 1.57 + 1.58 + // Test no id 1.59 + assert.throws( 1.60 + () => ToggleButton({ label: 'my button', icon: './icon.png' }), 1.61 + /^The option "id"/, 1.62 + 'throws on no id given'); 1.63 + 1.64 + // Test no icon 1.65 + assert.throws( 1.66 + () => ToggleButton({ id: 'my-button', label: 'my button' }), 1.67 + /^The option "icon"/, 1.68 + 'throws on no icon given'); 1.69 + 1.70 + 1.71 + // Test empty label 1.72 + assert.throws( 1.73 + () => ToggleButton({ id: 'my-button', label: '', icon: './icon.png' }), 1.74 + /^The option "label"/, 1.75 + 'throws on no valid label given'); 1.76 + 1.77 + // Test invalid id 1.78 + assert.throws( 1.79 + () => ToggleButton({ id: 'my button', label: 'my button', icon: './icon.png' }), 1.80 + /^The option "id"/, 1.81 + 'throws on no valid id given'); 1.82 + 1.83 + // Test empty id 1.84 + assert.throws( 1.85 + () => ToggleButton({ id: '', label: 'my button', icon: './icon.png' }), 1.86 + /^The option "id"/, 1.87 + 'throws on no valid id given'); 1.88 + 1.89 + // Test remote icon 1.90 + assert.throws( 1.91 + () => ToggleButton({ id: 'my-button', label: 'my button', icon: 'http://www.mozilla.org/favicon.ico'}), 1.92 + /^The option "icon"/, 1.93 + 'throws on no valid icon given'); 1.94 + 1.95 + // Test wrong icon: no absolute URI to local resource, neither relative './' 1.96 + assert.throws( 1.97 + () => ToggleButton({ id: 'my-button', label: 'my button', icon: 'icon.png'}), 1.98 + /^The option "icon"/, 1.99 + 'throws on no valid icon given'); 1.100 + 1.101 + // Test wrong icon: no absolute URI to local resource, neither relative './' 1.102 + assert.throws( 1.103 + () => ToggleButton({ id: 'my-button', label: 'my button', icon: 'foo and bar'}), 1.104 + /^The option "icon"/, 1.105 + 'throws on no valid icon given'); 1.106 + 1.107 + // Test wrong icon: '../' is not allowed 1.108 + assert.throws( 1.109 + () => ToggleButton({ id: 'my-button', label: 'my button', icon: '../icon.png'}), 1.110 + /^The option "icon"/, 1.111 + 'throws on no valid icon given'); 1.112 + 1.113 + // Test wrong checked 1.114 + assert.throws( 1.115 + () => ToggleButton({ 1.116 + id: 'my-button', label: 'my button', icon: './icon.png', checked: 'yes'}), 1.117 + /^The option "checked"/, 1.118 + 'throws on no valid checked value given'); 1.119 + 1.120 + loader.unload(); 1.121 +}; 1.122 + 1.123 +exports['test button added'] = function(assert) { 1.124 + let loader = Loader(module); 1.125 + let { ToggleButton } = loader.require('sdk/ui'); 1.126 + 1.127 + let button = ToggleButton({ 1.128 + id: 'my-button-1', 1.129 + label: 'my button', 1.130 + icon: './icon.png' 1.131 + }); 1.132 + 1.133 + // check defaults 1.134 + assert.equal(button.checked, false, 1.135 + 'checked is set to default `false` value'); 1.136 + 1.137 + assert.equal(button.disabled, false, 1.138 + 'disabled is set to default `false` value'); 1.139 + 1.140 + let { node } = getWidget(button.id); 1.141 + 1.142 + assert.ok(!!node, 'The button is in the navbar'); 1.143 + 1.144 + assert.equal(button.label, node.getAttribute('label'), 1.145 + 'label is set'); 1.146 + 1.147 + assert.equal(button.label, node.getAttribute('tooltiptext'), 1.148 + 'tooltip is set'); 1.149 + 1.150 + assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'), 1.151 + 'icon is set'); 1.152 + 1.153 + loader.unload(); 1.154 +} 1.155 + 1.156 +exports['test button added with resource URI'] = function(assert) { 1.157 + let loader = Loader(module); 1.158 + let { ToggleButton } = loader.require('sdk/ui'); 1.159 + 1.160 + let button = ToggleButton({ 1.161 + id: 'my-button-1', 1.162 + label: 'my button', 1.163 + icon: data.url('icon.png') 1.164 + }); 1.165 + 1.166 + assert.equal(button.icon, data.url('icon.png'), 1.167 + 'icon is set'); 1.168 + 1.169 + let { node } = getWidget(button.id); 1.170 + 1.171 + assert.equal(button.icon, node.getAttribute('image'), 1.172 + 'icon on node is set'); 1.173 + 1.174 + loader.unload(); 1.175 +} 1.176 + 1.177 +exports['test button duplicate id'] = function(assert) { 1.178 + let loader = Loader(module); 1.179 + let { ToggleButton } = loader.require('sdk/ui'); 1.180 + 1.181 + let button = ToggleButton({ 1.182 + id: 'my-button-2', 1.183 + label: 'my button', 1.184 + icon: './icon.png' 1.185 + }); 1.186 + 1.187 + assert.throws(() => { 1.188 + let doppelganger = ToggleButton({ 1.189 + id: 'my-button-2', 1.190 + label: 'my button', 1.191 + icon: './icon.png' 1.192 + }); 1.193 + }, 1.194 + /^The ID/, 1.195 + 'No duplicates allowed'); 1.196 + 1.197 + loader.unload(); 1.198 +} 1.199 + 1.200 +exports['test button multiple destroy'] = function(assert) { 1.201 + let loader = Loader(module); 1.202 + let { ToggleButton } = loader.require('sdk/ui'); 1.203 + 1.204 + let button = ToggleButton({ 1.205 + id: 'my-button-2', 1.206 + label: 'my button', 1.207 + icon: './icon.png' 1.208 + }); 1.209 + 1.210 + button.destroy(); 1.211 + button.destroy(); 1.212 + button.destroy(); 1.213 + 1.214 + assert.pass('multiple destroy doesn\'t matter'); 1.215 + 1.216 + loader.unload(); 1.217 +} 1.218 + 1.219 +exports['test button removed on dispose'] = function(assert, done) { 1.220 + const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); 1.221 + let loader = Loader(module); 1.222 + let { ToggleButton } = loader.require('sdk/ui'); 1.223 + 1.224 + let widgetId; 1.225 + 1.226 + CustomizableUI.addListener({ 1.227 + onWidgetDestroyed: function(id) { 1.228 + if (id === widgetId) { 1.229 + CustomizableUI.removeListener(this); 1.230 + 1.231 + assert.pass('button properly removed'); 1.232 + loader.unload(); 1.233 + done(); 1.234 + } 1.235 + } 1.236 + }); 1.237 + 1.238 + let button = ToggleButton({ 1.239 + id: 'my-button-3', 1.240 + label: 'my button', 1.241 + icon: './icon.png' 1.242 + }); 1.243 + 1.244 + // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it 1.245 + // was removed or it's not in the UX build yet 1.246 + widgetId = getWidget(button.id).id; 1.247 + 1.248 + button.destroy(); 1.249 +}; 1.250 + 1.251 +exports['test button global state updated'] = function(assert) { 1.252 + let loader = Loader(module); 1.253 + let { ToggleButton } = loader.require('sdk/ui'); 1.254 + 1.255 + let button = ToggleButton({ 1.256 + id: 'my-button-4', 1.257 + label: 'my button', 1.258 + icon: './icon.png' 1.259 + }); 1.260 + 1.261 + // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it 1.262 + // was removed or it's not in the UX build yet 1.263 + 1.264 + let { node, id: widgetId } = getWidget(button.id); 1.265 + 1.266 + // check read-only properties 1.267 + 1.268 + assert.throws(() => button.id = 'another-id', 1.269 + /^setting a property that has only a getter/, 1.270 + 'id cannot be set at runtime'); 1.271 + 1.272 + assert.equal(button.id, 'my-button-4', 1.273 + 'id is unchanged'); 1.274 + assert.equal(node.id, widgetId, 1.275 + 'node id is unchanged'); 1.276 + 1.277 + // check writable properties 1.278 + 1.279 + button.label = 'New label'; 1.280 + assert.equal(button.label, 'New label', 1.281 + 'label is updated'); 1.282 + assert.equal(node.getAttribute('label'), 'New label', 1.283 + 'node label is updated'); 1.284 + assert.equal(node.getAttribute('tooltiptext'), 'New label', 1.285 + 'node tooltip is updated'); 1.286 + 1.287 + button.icon = './new-icon.png'; 1.288 + assert.equal(button.icon, './new-icon.png', 1.289 + 'icon is updated'); 1.290 + assert.equal(node.getAttribute('image'), data.url('new-icon.png'), 1.291 + 'node image is updated'); 1.292 + 1.293 + button.disabled = true; 1.294 + assert.equal(button.disabled, true, 1.295 + 'disabled is updated'); 1.296 + assert.equal(node.getAttribute('disabled'), 'true', 1.297 + 'node disabled is updated'); 1.298 + 1.299 + // TODO: test validation on update 1.300 + 1.301 + loader.unload(); 1.302 +} 1.303 + 1.304 +exports['test button global state set and get with state method'] = function(assert) { 1.305 + let loader = Loader(module); 1.306 + let { ToggleButton } = loader.require('sdk/ui'); 1.307 + 1.308 + let button = ToggleButton({ 1.309 + id: 'my-button-16', 1.310 + label: 'my button', 1.311 + icon: './icon.png' 1.312 + }); 1.313 + 1.314 + // read the button's state 1.315 + let state = button.state(button); 1.316 + 1.317 + assert.equal(state.label, 'my button', 1.318 + 'label is correct'); 1.319 + assert.equal(state.icon, './icon.png', 1.320 + 'icon is correct'); 1.321 + assert.equal(state.disabled, false, 1.322 + 'disabled is correct'); 1.323 + 1.324 + // set the new button's state 1.325 + button.state(button, { 1.326 + label: 'New label', 1.327 + icon: './new-icon.png', 1.328 + disabled: true 1.329 + }); 1.330 + 1.331 + assert.equal(button.label, 'New label', 1.332 + 'label is updated'); 1.333 + assert.equal(button.icon, './new-icon.png', 1.334 + 'icon is updated'); 1.335 + assert.equal(button.disabled, true, 1.336 + 'disabled is updated'); 1.337 + 1.338 + loader.unload(); 1.339 +}; 1.340 + 1.341 +exports['test button global state updated on multiple windows'] = function(assert, done) { 1.342 + let loader = Loader(module); 1.343 + let { ToggleButton } = loader.require('sdk/ui'); 1.344 + 1.345 + let button = ToggleButton({ 1.346 + id: 'my-button-5', 1.347 + label: 'my button', 1.348 + icon: './icon.png' 1.349 + }); 1.350 + 1.351 + let nodes = [getWidget(button.id).node]; 1.352 + 1.353 + openBrowserWindow().then(window => { 1.354 + nodes.push(getWidget(button.id, window).node); 1.355 + 1.356 + button.label = 'New label'; 1.357 + button.icon = './new-icon.png'; 1.358 + button.disabled = true; 1.359 + 1.360 + for (let node of nodes) { 1.361 + assert.equal(node.getAttribute('label'), 'New label', 1.362 + 'node label is updated'); 1.363 + assert.equal(node.getAttribute('tooltiptext'), 'New label', 1.364 + 'node tooltip is updated'); 1.365 + 1.366 + assert.equal(button.icon, './new-icon.png', 1.367 + 'icon is updated'); 1.368 + assert.equal(node.getAttribute('image'), data.url('new-icon.png'), 1.369 + 'node image is updated'); 1.370 + 1.371 + assert.equal(button.disabled, true, 1.372 + 'disabled is updated'); 1.373 + assert.equal(node.getAttribute('disabled'), 'true', 1.374 + 'node disabled is updated'); 1.375 + }; 1.376 + 1.377 + return window; 1.378 + }). 1.379 + then(close). 1.380 + then(loader.unload). 1.381 + then(done, assert.fail); 1.382 +}; 1.383 + 1.384 +exports['test button window state'] = function(assert, done) { 1.385 + let loader = Loader(module); 1.386 + let { ToggleButton } = loader.require('sdk/ui'); 1.387 + let { browserWindows } = loader.require('sdk/windows'); 1.388 + 1.389 + let button = ToggleButton({ 1.390 + id: 'my-button-6', 1.391 + label: 'my button', 1.392 + icon: './icon.png' 1.393 + }); 1.394 + 1.395 + let mainWindow = browserWindows.activeWindow; 1.396 + let nodes = [getWidget(button.id).node]; 1.397 + 1.398 + openBrowserWindow().then(focus).then(window => { 1.399 + nodes.push(getWidget(button.id, window).node); 1.400 + 1.401 + let { activeWindow } = browserWindows; 1.402 + 1.403 + button.state(activeWindow, { 1.404 + label: 'New label', 1.405 + icon: './new-icon.png', 1.406 + disabled: true 1.407 + }); 1.408 + 1.409 + // check the states 1.410 + 1.411 + assert.equal(button.label, 'my button', 1.412 + 'global label unchanged'); 1.413 + assert.equal(button.icon, './icon.png', 1.414 + 'global icon unchanged'); 1.415 + assert.equal(button.disabled, false, 1.416 + 'global disabled unchanged'); 1.417 + 1.418 + let state = button.state(mainWindow); 1.419 + 1.420 + assert.equal(state.label, 'my button', 1.421 + 'previous window label unchanged'); 1.422 + assert.equal(state.icon, './icon.png', 1.423 + 'previous window icon unchanged'); 1.424 + assert.equal(state.disabled, false, 1.425 + 'previous window disabled unchanged'); 1.426 + 1.427 + let state = button.state(activeWindow); 1.428 + 1.429 + assert.equal(state.label, 'New label', 1.430 + 'active window label updated'); 1.431 + assert.equal(state.icon, './new-icon.png', 1.432 + 'active window icon updated'); 1.433 + assert.equal(state.disabled, true, 1.434 + 'active disabled updated'); 1.435 + 1.436 + // change the global state, only the windows without a state are affected 1.437 + 1.438 + button.label = 'A good label'; 1.439 + 1.440 + assert.equal(button.label, 'A good label', 1.441 + 'global label updated'); 1.442 + assert.equal(button.state(mainWindow).label, 'A good label', 1.443 + 'previous window label updated'); 1.444 + assert.equal(button.state(activeWindow).label, 'New label', 1.445 + 'active window label unchanged'); 1.446 + 1.447 + // delete the window state will inherits the global state again 1.448 + 1.449 + button.state(activeWindow, null); 1.450 + 1.451 + assert.equal(button.state(activeWindow).label, 'A good label', 1.452 + 'active window label inherited'); 1.453 + 1.454 + // check the nodes properties 1.455 + let node = nodes[0]; 1.456 + let state = button.state(mainWindow); 1.457 + 1.458 + assert.equal(node.getAttribute('label'), state.label, 1.459 + 'node label is correct'); 1.460 + assert.equal(node.getAttribute('tooltiptext'), state.label, 1.461 + 'node tooltip is correct'); 1.462 + 1.463 + assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 1.464 + 'node image is correct'); 1.465 + assert.equal(node.hasAttribute('disabled'), state.disabled, 1.466 + 'disabled is correct'); 1.467 + 1.468 + let node = nodes[1]; 1.469 + let state = button.state(activeWindow); 1.470 + 1.471 + assert.equal(node.getAttribute('label'), state.label, 1.472 + 'node label is correct'); 1.473 + assert.equal(node.getAttribute('tooltiptext'), state.label, 1.474 + 'node tooltip is correct'); 1.475 + 1.476 + assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 1.477 + 'node image is correct'); 1.478 + assert.equal(node.hasAttribute('disabled'), state.disabled, 1.479 + 'disabled is correct'); 1.480 + 1.481 + return window; 1.482 + }). 1.483 + then(close). 1.484 + then(loader.unload). 1.485 + then(done, assert.fail); 1.486 +}; 1.487 + 1.488 + 1.489 +exports['test button tab state'] = function(assert, done) { 1.490 + let loader = Loader(module); 1.491 + let { ToggleButton } = loader.require('sdk/ui'); 1.492 + let { browserWindows } = loader.require('sdk/windows'); 1.493 + let tabs = loader.require('sdk/tabs'); 1.494 + 1.495 + let button = ToggleButton({ 1.496 + id: 'my-button-7', 1.497 + label: 'my button', 1.498 + icon: './icon.png' 1.499 + }); 1.500 + 1.501 + let mainTab = tabs.activeTab; 1.502 + let node = getWidget(button.id).node; 1.503 + 1.504 + tabs.open({ 1.505 + url: 'about:blank', 1.506 + onActivate: function onActivate(tab) { 1.507 + tab.removeListener('activate', onActivate); 1.508 + 1.509 + let { activeWindow } = browserWindows; 1.510 + // set window state 1.511 + button.state(activeWindow, { 1.512 + label: 'Window label', 1.513 + icon: './window-icon.png' 1.514 + }); 1.515 + 1.516 + // set previous active tab state 1.517 + button.state(mainTab, { 1.518 + label: 'Tab label', 1.519 + icon: './tab-icon.png', 1.520 + }); 1.521 + 1.522 + // set current active tab state 1.523 + button.state(tab, { 1.524 + icon: './another-tab-icon.png', 1.525 + disabled: true 1.526 + }); 1.527 + 1.528 + // check the states 1.529 + 1.530 + Cu.schedulePreciseGC(() => { 1.531 + assert.equal(button.label, 'my button', 1.532 + 'global label unchanged'); 1.533 + assert.equal(button.icon, './icon.png', 1.534 + 'global icon unchanged'); 1.535 + assert.equal(button.disabled, false, 1.536 + 'global disabled unchanged'); 1.537 + 1.538 + let state = button.state(mainTab); 1.539 + 1.540 + assert.equal(state.label, 'Tab label', 1.541 + 'previous tab label updated'); 1.542 + assert.equal(state.icon, './tab-icon.png', 1.543 + 'previous tab icon updated'); 1.544 + assert.equal(state.disabled, false, 1.545 + 'previous tab disabled unchanged'); 1.546 + 1.547 + let state = button.state(tab); 1.548 + 1.549 + assert.equal(state.label, 'Window label', 1.550 + 'active tab inherited from window state'); 1.551 + assert.equal(state.icon, './another-tab-icon.png', 1.552 + 'active tab icon updated'); 1.553 + assert.equal(state.disabled, true, 1.554 + 'active disabled updated'); 1.555 + 1.556 + // change the global state 1.557 + button.icon = './good-icon.png'; 1.558 + 1.559 + // delete the tab state 1.560 + button.state(tab, null); 1.561 + 1.562 + assert.equal(button.icon, './good-icon.png', 1.563 + 'global icon updated'); 1.564 + assert.equal(button.state(mainTab).icon, './tab-icon.png', 1.565 + 'previous tab icon unchanged'); 1.566 + assert.equal(button.state(tab).icon, './window-icon.png', 1.567 + 'tab icon inherited from window'); 1.568 + 1.569 + // delete the window state 1.570 + button.state(activeWindow, null); 1.571 + 1.572 + assert.equal(button.state(tab).icon, './good-icon.png', 1.573 + 'tab icon inherited from global'); 1.574 + 1.575 + // check the node properties 1.576 + 1.577 + let state = button.state(tabs.activeTab); 1.578 + 1.579 + assert.equal(node.getAttribute('label'), state.label, 1.580 + 'node label is correct'); 1.581 + assert.equal(node.getAttribute('tooltiptext'), state.label, 1.582 + 'node tooltip is correct'); 1.583 + assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 1.584 + 'node image is correct'); 1.585 + assert.equal(node.hasAttribute('disabled'), state.disabled, 1.586 + 'disabled is correct'); 1.587 + 1.588 + tabs.once('activate', () => { 1.589 + // This is made in order to avoid to check the node before it 1.590 + // is updated, need a better check 1.591 + setTimeout(() => { 1.592 + let state = button.state(mainTab); 1.593 + 1.594 + assert.equal(node.getAttribute('label'), state.label, 1.595 + 'node label is correct'); 1.596 + assert.equal(node.getAttribute('tooltiptext'), state.label, 1.597 + 'node tooltip is correct'); 1.598 + assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)), 1.599 + 'node image is correct'); 1.600 + assert.equal(node.hasAttribute('disabled'), state.disabled, 1.601 + 'disabled is correct'); 1.602 + 1.603 + tab.close(() => { 1.604 + loader.unload(); 1.605 + done(); 1.606 + }); 1.607 + }, 500); 1.608 + }); 1.609 + 1.610 + mainTab.activate(); 1.611 + }); 1.612 + } 1.613 + }); 1.614 + 1.615 +}; 1.616 + 1.617 +exports['test button click'] = function(assert, done) { 1.618 + let loader = Loader(module); 1.619 + let { ToggleButton } = loader.require('sdk/ui'); 1.620 + let { browserWindows } = loader.require('sdk/windows'); 1.621 + 1.622 + let labels = []; 1.623 + 1.624 + let button = ToggleButton({ 1.625 + id: 'my-button-8', 1.626 + label: 'my button', 1.627 + icon: './icon.png', 1.628 + onClick: ({label}) => labels.push(label) 1.629 + }); 1.630 + 1.631 + let mainWindow = browserWindows.activeWindow; 1.632 + let chromeWindow = getMostRecentBrowserWindow(); 1.633 + 1.634 + openBrowserWindow().then(focus).then(window => { 1.635 + button.state(mainWindow, { label: 'nothing' }); 1.636 + button.state(mainWindow.tabs.activeTab, { label: 'foo'}) 1.637 + button.state(browserWindows.activeWindow, { label: 'bar' }); 1.638 + 1.639 + button.click(); 1.640 + 1.641 + focus(chromeWindow).then(() => { 1.642 + button.click(); 1.643 + 1.644 + assert.deepEqual(labels, ['bar', 'foo'], 1.645 + 'button click works'); 1.646 + 1.647 + close(window). 1.648 + then(loader.unload). 1.649 + then(done, assert.fail); 1.650 + }); 1.651 + }).then(null, assert.fail); 1.652 +} 1.653 + 1.654 +exports['test button icon set'] = function(assert) { 1.655 + const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); 1.656 + let loader = Loader(module); 1.657 + let { ToggleButton } = loader.require('sdk/ui'); 1.658 + 1.659 + // Test remote icon set 1.660 + assert.throws( 1.661 + () => ToggleButton({ 1.662 + id: 'my-button-10', 1.663 + label: 'my button', 1.664 + icon: { 1.665 + '16': 'http://www.mozilla.org/favicon.ico' 1.666 + } 1.667 + }), 1.668 + /^The option "icon"/, 1.669 + 'throws on no valid icon given'); 1.670 + 1.671 + let button = ToggleButton({ 1.672 + id: 'my-button-11', 1.673 + label: 'my button', 1.674 + icon: { 1.675 + '5': './icon5.png', 1.676 + '16': './icon16.png', 1.677 + '32': './icon32.png', 1.678 + '64': './icon64.png' 1.679 + } 1.680 + }); 1.681 + 1.682 + let { node, id: widgetId } = getWidget(button.id); 1.683 + let { devicePixelRatio } = node.ownerDocument.defaultView; 1.684 + 1.685 + let size = 16 * devicePixelRatio; 1.686 + 1.687 + assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)), 1.688 + 'the icon is set properly in navbar'); 1.689 + 1.690 + let size = 32 * devicePixelRatio; 1.691 + 1.692 + CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL); 1.693 + 1.694 + assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)), 1.695 + 'the icon is set properly in panel'); 1.696 + 1.697 + // Using `loader.unload` without move back the button to the original area 1.698 + // raises an error in the CustomizableUI. This is doesn't happen if the 1.699 + // button is moved manually from navbar to panel. I believe it has to do 1.700 + // with `addWidgetToArea` method, because even with a `timeout` the issue 1.701 + // persist. 1.702 + CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR); 1.703 + 1.704 + loader.unload(); 1.705 +} 1.706 + 1.707 +exports['test button icon se with only one option'] = function(assert) { 1.708 + const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); 1.709 + let loader = Loader(module); 1.710 + let { ToggleButton } = loader.require('sdk/ui'); 1.711 + 1.712 + // Test remote icon set 1.713 + assert.throws( 1.714 + () => ToggleButton({ 1.715 + id: 'my-button-10', 1.716 + label: 'my button', 1.717 + icon: { 1.718 + '16': 'http://www.mozilla.org/favicon.ico' 1.719 + } 1.720 + }), 1.721 + /^The option "icon"/, 1.722 + 'throws on no valid icon given'); 1.723 + 1.724 + let button = ToggleButton({ 1.725 + id: 'my-button-11', 1.726 + label: 'my button', 1.727 + icon: { 1.728 + '5': './icon5.png' 1.729 + } 1.730 + }); 1.731 + 1.732 + let { node, id: widgetId } = getWidget(button.id); 1.733 + 1.734 + assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)), 1.735 + 'the icon is set properly in navbar'); 1.736 + 1.737 + CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL); 1.738 + 1.739 + assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)), 1.740 + 'the icon is set properly in panel'); 1.741 + 1.742 + // Using `loader.unload` without move back the button to the original area 1.743 + // raises an error in the CustomizableUI. This is doesn't happen if the 1.744 + // button is moved manually from navbar to panel. I believe it has to do 1.745 + // with `addWidgetToArea` method, because even with a `timeout` the issue 1.746 + // persist. 1.747 + CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR); 1.748 + 1.749 + loader.unload(); 1.750 +} 1.751 + 1.752 +exports['test button state validation'] = function(assert) { 1.753 + let loader = Loader(module); 1.754 + let { ToggleButton } = loader.require('sdk/ui'); 1.755 + let { browserWindows } = loader.require('sdk/windows'); 1.756 + 1.757 + let button = ToggleButton({ 1.758 + id: 'my-button-12', 1.759 + label: 'my button', 1.760 + icon: './icon.png' 1.761 + }) 1.762 + 1.763 + let state = button.state(button); 1.764 + 1.765 + assert.throws( 1.766 + () => button.state(button, { icon: 'http://www.mozilla.org/favicon.ico' }), 1.767 + /^The option "icon"/, 1.768 + 'throws on remote icon given'); 1.769 + 1.770 + loader.unload(); 1.771 +}; 1.772 + 1.773 +exports['test button are not in private windows'] = function(assert, done) { 1.774 + let loader = Loader(module); 1.775 + let { ToggleButton } = loader.require('sdk/ui'); 1.776 + let{ isPrivate } = loader.require('sdk/private-browsing'); 1.777 + let { browserWindows } = loader.require('sdk/windows'); 1.778 + 1.779 + let button = ToggleButton({ 1.780 + id: 'my-button-13', 1.781 + label: 'my button', 1.782 + icon: './icon.png' 1.783 + }); 1.784 + 1.785 + openPrivateBrowserWindow().then(window => { 1.786 + assert.ok(isPrivate(window), 1.787 + 'the new window is private'); 1.788 + 1.789 + let { node } = getWidget(button.id, window); 1.790 + 1.791 + assert.ok(!node || node.style.display === 'none', 1.792 + 'the button is not added / is not visible on private window'); 1.793 + 1.794 + return window; 1.795 + }). 1.796 + then(close). 1.797 + then(loader.unload). 1.798 + then(done, assert.fail) 1.799 +} 1.800 + 1.801 +exports['test button state are snapshot'] = function(assert) { 1.802 + let loader = Loader(module); 1.803 + let { ToggleButton } = loader.require('sdk/ui'); 1.804 + let { browserWindows } = loader.require('sdk/windows'); 1.805 + let tabs = loader.require('sdk/tabs'); 1.806 + 1.807 + let button = ToggleButton({ 1.808 + id: 'my-button-14', 1.809 + label: 'my button', 1.810 + icon: './icon.png' 1.811 + }); 1.812 + 1.813 + let state = button.state(button); 1.814 + let windowState = button.state(browserWindows.activeWindow); 1.815 + let tabState = button.state(tabs.activeTab); 1.816 + 1.817 + assert.deepEqual(windowState, state, 1.818 + 'window state has the same properties of button state'); 1.819 + 1.820 + assert.deepEqual(tabState, state, 1.821 + 'tab state has the same properties of button state'); 1.822 + 1.823 + assert.notEqual(windowState, state, 1.824 + 'window state is not the same object of button state'); 1.825 + 1.826 + assert.notEqual(tabState, state, 1.827 + 'tab state is not the same object of button state'); 1.828 + 1.829 + assert.deepEqual(button.state(button), state, 1.830 + 'button state has the same content of previous button state'); 1.831 + 1.832 + assert.deepEqual(button.state(browserWindows.activeWindow), windowState, 1.833 + 'window state has the same content of previous window state'); 1.834 + 1.835 + assert.deepEqual(button.state(tabs.activeTab), tabState, 1.836 + 'tab state has the same content of previous tab state'); 1.837 + 1.838 + assert.notEqual(button.state(button), state, 1.839 + 'button state is not the same object of previous button state'); 1.840 + 1.841 + assert.notEqual(button.state(browserWindows.activeWindow), windowState, 1.842 + 'window state is not the same object of previous window state'); 1.843 + 1.844 + assert.notEqual(button.state(tabs.activeTab), tabState, 1.845 + 'tab state is not the same object of previous tab state'); 1.846 + 1.847 + loader.unload(); 1.848 +} 1.849 + 1.850 +exports['test button icon object is a snapshot'] = function(assert) { 1.851 + let loader = Loader(module); 1.852 + let { ToggleButton } = loader.require('sdk/ui'); 1.853 + 1.854 + let icon = { 1.855 + '16': './foo.png' 1.856 + }; 1.857 + 1.858 + let button = ToggleButton({ 1.859 + id: 'my-button-17', 1.860 + label: 'my button', 1.861 + icon: icon 1.862 + }); 1.863 + 1.864 + assert.deepEqual(button.icon, icon, 1.865 + 'button.icon has the same properties of the object set in the constructor'); 1.866 + 1.867 + assert.notEqual(button.icon, icon, 1.868 + 'button.icon is not the same object of the object set in the constructor'); 1.869 + 1.870 + assert.throws( 1.871 + () => button.icon[16] = './bar.png', 1.872 + /16 is read-only/, 1.873 + 'properties of button.icon are ready-only' 1.874 + ); 1.875 + 1.876 + let newIcon = {'16': './bar.png'}; 1.877 + button.icon = newIcon; 1.878 + 1.879 + assert.deepEqual(button.icon, newIcon, 1.880 + 'button.icon has the same properties of the object set'); 1.881 + 1.882 + assert.notEqual(button.icon, newIcon, 1.883 + 'button.icon is not the same object of the object set'); 1.884 + 1.885 + loader.unload(); 1.886 +} 1.887 + 1.888 +exports['test button after destroy'] = function(assert) { 1.889 + let loader = Loader(module); 1.890 + let { ToggleButton } = loader.require('sdk/ui'); 1.891 + let { browserWindows } = loader.require('sdk/windows'); 1.892 + let { activeTab } = loader.require('sdk/tabs'); 1.893 + 1.894 + let button = ToggleButton({ 1.895 + id: 'my-button-15', 1.896 + label: 'my button', 1.897 + icon: './icon.png', 1.898 + onClick: () => assert.fail('onClick should not be called') 1.899 + }); 1.900 + 1.901 + button.destroy(); 1.902 + 1.903 + assert.throws( 1.904 + () => button.click(), 1.905 + /^The state cannot be set or get/, 1.906 + 'button.click() not executed'); 1.907 + 1.908 + assert.throws( 1.909 + () => button.label, 1.910 + /^The state cannot be set or get/, 1.911 + 'button.label cannot be get after destroy'); 1.912 + 1.913 + assert.throws( 1.914 + () => button.label = 'my label', 1.915 + /^The state cannot be set or get/, 1.916 + 'button.label cannot be set after destroy'); 1.917 + 1.918 + assert.throws( 1.919 + () => { 1.920 + button.state(browserWindows.activeWindow, { 1.921 + label: 'window label' 1.922 + }); 1.923 + }, 1.924 + /^The state cannot be set or get/, 1.925 + 'window state label cannot be set after destroy'); 1.926 + 1.927 + assert.throws( 1.928 + () => button.state(browserWindows.activeWindow).label, 1.929 + /^The state cannot be set or get/, 1.930 + 'window state label cannot be get after destroy'); 1.931 + 1.932 + assert.throws( 1.933 + () => { 1.934 + button.state(activeTab, { 1.935 + label: 'tab label' 1.936 + }); 1.937 + }, 1.938 + /^The state cannot be set or get/, 1.939 + 'tab state label cannot be set after destroy'); 1.940 + 1.941 + assert.throws( 1.942 + () => button.state(activeTab).label, 1.943 + /^The state cannot be set or get/, 1.944 + 'window state label cannot se get after destroy'); 1.945 + 1.946 + loader.unload(); 1.947 +}; 1.948 + 1.949 +exports['test button checked'] = function(assert, done) { 1.950 + let loader = Loader(module); 1.951 + let { ToggleButton } = loader.require('sdk/ui'); 1.952 + let { browserWindows } = loader.require('sdk/windows'); 1.953 + 1.954 + let events = []; 1.955 + 1.956 + let button = ToggleButton({ 1.957 + id: 'my-button-9', 1.958 + label: 'my button', 1.959 + icon: './icon.png', 1.960 + checked: true, 1.961 + onClick: ({label}) => events.push('clicked:' + label), 1.962 + onChange: state => events.push('changed:' + state.label + ':' + state.checked) 1.963 + }); 1.964 + 1.965 + let { node } = getWidget(button.id); 1.966 + 1.967 + assert.equal(node.getAttribute('type'), 'checkbox', 1.968 + 'node type is properly set'); 1.969 + 1.970 + let mainWindow = browserWindows.activeWindow; 1.971 + let chromeWindow = getMostRecentBrowserWindow(); 1.972 + 1.973 + openBrowserWindow().then(focus).then(window => { 1.974 + button.state(mainWindow, { label: 'nothing' }); 1.975 + button.state(mainWindow.tabs.activeTab, { label: 'foo'}) 1.976 + button.state(browserWindows.activeWindow, { label: 'bar' }); 1.977 + 1.978 + button.click(); 1.979 + button.click(); 1.980 + 1.981 + focus(chromeWindow).then(() => { 1.982 + button.click(); 1.983 + button.click(); 1.984 + 1.985 + assert.deepEqual(events, [ 1.986 + 'clicked:bar', 'changed:bar:false', 'clicked:bar', 'changed:bar:true', 1.987 + 'clicked:foo', 'changed:foo:false', 'clicked:foo', 'changed:foo:true' 1.988 + ], 1.989 + 'button change events works'); 1.990 + 1.991 + close(window). 1.992 + then(loader.unload). 1.993 + then(done, assert.fail); 1.994 + }) 1.995 + }).then(null, assert.fail); 1.996 +} 1.997 + 1.998 +exports['test button is checked on window level'] = function(assert, done) { 1.999 + let loader = Loader(module); 1.1000 + let { ToggleButton } = loader.require('sdk/ui'); 1.1001 + let { browserWindows } = loader.require('sdk/windows'); 1.1002 + let tabs = loader.require('sdk/tabs'); 1.1003 + 1.1004 + let button = ToggleButton({ 1.1005 + id: 'my-button-20', 1.1006 + label: 'my button', 1.1007 + icon: './icon.png' 1.1008 + }); 1.1009 + 1.1010 + let mainWindow = browserWindows.activeWindow; 1.1011 + let mainTab = tabs.activeTab; 1.1012 + 1.1013 + assert.equal(button.checked, false, 1.1014 + 'global state, checked is `false`.'); 1.1015 + assert.equal(button.state(mainTab).checked, false, 1.1016 + 'tab state, checked is `false`.'); 1.1017 + assert.equal(button.state(mainWindow).checked, false, 1.1018 + 'window state, checked is `false`.'); 1.1019 + 1.1020 + button.click(); 1.1021 + 1.1022 + tabs.open({ 1.1023 + url: 'about:blank', 1.1024 + onActivate: function onActivate(tab) { 1.1025 + tab.removeListener('activate', onActivate); 1.1026 + 1.1027 + assert.notEqual(mainTab, tab, 1.1028 + 'the current tab is not the same.'); 1.1029 + 1.1030 + assert.equal(button.checked, false, 1.1031 + 'global state, checked is `false`.'); 1.1032 + assert.equal(button.state(mainTab).checked, true, 1.1033 + 'previous tab state, checked is `true`.'); 1.1034 + assert.equal(button.state(tab).checked, true, 1.1035 + 'current tab state, checked is `true`.'); 1.1036 + assert.equal(button.state(mainWindow).checked, true, 1.1037 + 'window state, checked is `true`.'); 1.1038 + 1.1039 + openBrowserWindow().then(focus).then(window => { 1.1040 + let { activeWindow } = browserWindows; 1.1041 + let { activeTab } = activeWindow.tabs; 1.1042 + 1.1043 + assert.equal(button.checked, false, 1.1044 + 'global state, checked is `false`.'); 1.1045 + assert.equal(button.state(activeTab).checked, false, 1.1046 + 'tab state, checked is `false`.'); 1.1047 + 1.1048 + assert.equal(button.state(activeWindow).checked, false, 1.1049 + 'window state, checked is `false`.'); 1.1050 + 1.1051 + tab.close(()=> { 1.1052 + close(window). 1.1053 + then(loader.unload). 1.1054 + then(done, assert.fail); 1.1055 + }) 1.1056 + }). 1.1057 + then(null, assert.fail); 1.1058 + } 1.1059 + }); 1.1060 + 1.1061 +}; 1.1062 + 1.1063 +exports['test button click do not messing up states'] = function(assert) { 1.1064 + let loader = Loader(module); 1.1065 + let { ToggleButton } = loader.require('sdk/ui'); 1.1066 + let { browserWindows } = loader.require('sdk/windows'); 1.1067 + 1.1068 + let button = ToggleButton({ 1.1069 + id: 'my-button-21', 1.1070 + label: 'my button', 1.1071 + icon: './icon.png' 1.1072 + }); 1.1073 + 1.1074 + let mainWindow = browserWindows.activeWindow; 1.1075 + let { activeTab } = mainWindow.tabs; 1.1076 + 1.1077 + button.state(mainWindow, { icon: './new-icon.png' }); 1.1078 + button.state(activeTab, { label: 'foo'}) 1.1079 + 1.1080 + assert.equal(button.state(mainWindow).label, 'my button', 1.1081 + 'label property for window state, properly derived from global state'); 1.1082 + 1.1083 + assert.equal(button.state(activeTab).icon, './new-icon.png', 1.1084 + 'icon property for tab state, properly derived from window state'); 1.1085 + 1.1086 + button.click(); 1.1087 + 1.1088 + button.label = 'bar'; 1.1089 + 1.1090 + assert.equal(button.state(mainWindow).label, 'bar', 1.1091 + 'label property for window state, properly derived from global state'); 1.1092 + 1.1093 + button.state(mainWindow, null); 1.1094 + 1.1095 + assert.equal(button.state(activeTab).icon, './icon.png', 1.1096 + 'icon property for tab state, properly derived from window state'); 1.1097 + 1.1098 + loader.unload(); 1.1099 +} 1.1100 + 1.1101 +exports['test buttons can have anchored panels'] = function(assert, done) { 1.1102 + let loader = Loader(module); 1.1103 + let { ToggleButton } = loader.require('sdk/ui'); 1.1104 + let { Panel } = loader.require('sdk/panel'); 1.1105 + let { identify } = loader.require('sdk/ui/id'); 1.1106 + let { getActiveView } = loader.require('sdk/view/core'); 1.1107 + 1.1108 + let b1 = ToggleButton({ 1.1109 + id: 'my-button-22', 1.1110 + label: 'my button', 1.1111 + icon: './icon.png', 1.1112 + onChange: ({checked}) => checked && panel.show() 1.1113 + }); 1.1114 + 1.1115 + let b2 = ToggleButton({ 1.1116 + id: 'my-button-23', 1.1117 + label: 'my button', 1.1118 + icon: './icon.png', 1.1119 + onChange: ({checked}) => checked && panel.show({position: b2}) 1.1120 + }); 1.1121 + 1.1122 + let panel = Panel({ 1.1123 + position: b1 1.1124 + }); 1.1125 + 1.1126 + let { document } = getMostRecentBrowserWindow(); 1.1127 + let b1Node = document.getElementById(identify(b1)); 1.1128 + let b2Node = document.getElementById(identify(b2)); 1.1129 + let panelNode = getActiveView(panel); 1.1130 + 1.1131 + panel.once('show', () => { 1.1132 + assert.ok(b1.state('window').checked, 1.1133 + 'button is checked'); 1.1134 + 1.1135 + assert.equal(panelNode.getAttribute('type'), 'arrow', 1.1136 + 'the panel is a arrow type'); 1.1137 + 1.1138 + assert.strictEqual(b1Node, panelNode.anchorNode, 1.1139 + 'the panel is anchored properly to the button given in costructor'); 1.1140 + 1.1141 + panel.hide(); 1.1142 + 1.1143 + panel.once('show', () => { 1.1144 + assert.ok(b2.state('window').checked, 1.1145 + 'button is checked'); 1.1146 + 1.1147 + assert.equal(panelNode.getAttribute('type'), 'arrow', 1.1148 + 'the panel is a arrow type'); 1.1149 + 1.1150 + // test also that the button passed in `show` method, takes the precedence 1.1151 + // over the button set in panel's constructor. 1.1152 + assert.strictEqual(b2Node, panelNode.anchorNode, 1.1153 + 'the panel is anchored properly to the button passed to show method'); 1.1154 + 1.1155 + loader.unload(); 1.1156 + 1.1157 + done(); 1.1158 + }); 1.1159 + 1.1160 + b2.click(); 1.1161 + }); 1.1162 + 1.1163 + b1.click(); 1.1164 +} 1.1165 + 1.1166 +require('sdk/test').run(exports);