addon-sdk/source/test/test-ui-action-button.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/test/test-ui-action-button.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,937 @@
     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('action-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 { ActionButton } = loader.require('sdk/ui');
    1.46 +
    1.47 +  assert.throws(
    1.48 +    () => ActionButton({}),
    1.49 +    /^The option/,
    1.50 +    'throws on no option given');
    1.51 +
    1.52 +  // Test no label
    1.53 +  assert.throws(
    1.54 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +    () => ActionButton({ 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 +  loader.unload();
   1.114 +};
   1.115 +
   1.116 +exports['test button added'] = function(assert) {
   1.117 +  let loader = Loader(module);
   1.118 +  let { ActionButton } = loader.require('sdk/ui');
   1.119 +
   1.120 +  let button = ActionButton({
   1.121 +    id: 'my-button-1',
   1.122 +    label: 'my button',
   1.123 +    icon: './icon.png'
   1.124 +  });
   1.125 +
   1.126 +  // check defaults
   1.127 +  assert.equal(button.disabled, false,
   1.128 +    'disabled is set to default `false` value');
   1.129 +
   1.130 +  let { node } = getWidget(button.id);
   1.131 +
   1.132 +  assert.ok(!!node, 'The button is in the navbar');
   1.133 +
   1.134 +  assert.equal(button.label, node.getAttribute('label'),
   1.135 +    'label is set');
   1.136 +
   1.137 +  assert.equal(button.label, node.getAttribute('tooltiptext'),
   1.138 +    'tooltip is set');
   1.139 +
   1.140 +  assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'),
   1.141 +    'icon is set');
   1.142 +
   1.143 +  loader.unload();
   1.144 +}
   1.145 +
   1.146 +exports['test button added with resource URI'] = function(assert) {
   1.147 +  let loader = Loader(module);
   1.148 +  let { ActionButton } = loader.require('sdk/ui');
   1.149 +
   1.150 +  let button = ActionButton({
   1.151 +    id: 'my-button-1',
   1.152 +    label: 'my button',
   1.153 +    icon: data.url('icon.png')
   1.154 +  });
   1.155 +
   1.156 +  assert.equal(button.icon, data.url('icon.png'),
   1.157 +    'icon is set');
   1.158 +
   1.159 +  let { node } = getWidget(button.id);
   1.160 +
   1.161 +  assert.equal(button.icon, node.getAttribute('image'),
   1.162 +    'icon on node is set');
   1.163 +
   1.164 +  loader.unload();
   1.165 +}
   1.166 +
   1.167 +exports['test button duplicate id'] = function(assert) {
   1.168 +  let loader = Loader(module);
   1.169 +  let { ActionButton } = loader.require('sdk/ui');
   1.170 +
   1.171 +  let button = ActionButton({
   1.172 +    id: 'my-button-2',
   1.173 +    label: 'my button',
   1.174 +    icon: './icon.png'
   1.175 +  });
   1.176 +
   1.177 +  assert.throws(() => {
   1.178 +    let doppelganger = ActionButton({
   1.179 +      id: 'my-button-2',
   1.180 +      label: 'my button',
   1.181 +      icon: './icon.png'
   1.182 +    });
   1.183 +  },
   1.184 +  /^The ID/,
   1.185 +  'No duplicates allowed');
   1.186 +
   1.187 +  loader.unload();
   1.188 +}
   1.189 +
   1.190 +exports['test button multiple destroy'] = function(assert) {
   1.191 +  let loader = Loader(module);
   1.192 +  let { ActionButton } = loader.require('sdk/ui');
   1.193 +
   1.194 +  let button = ActionButton({
   1.195 +    id: 'my-button-2',
   1.196 +    label: 'my button',
   1.197 +    icon: './icon.png'
   1.198 +  });
   1.199 +
   1.200 +  button.destroy();
   1.201 +  button.destroy();
   1.202 +  button.destroy();
   1.203 +
   1.204 +  assert.pass('multiple destroy doesn\'t matter');
   1.205 +
   1.206 +  loader.unload();
   1.207 +}
   1.208 +
   1.209 +exports['test button removed on dispose'] = function(assert, done) {
   1.210 +  const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
   1.211 +  let loader = Loader(module);
   1.212 +  let { ActionButton } = loader.require('sdk/ui');
   1.213 +
   1.214 +  let widgetId;
   1.215 +
   1.216 +  CustomizableUI.addListener({
   1.217 +    onWidgetDestroyed: function(id) {
   1.218 +      if (id === widgetId) {
   1.219 +        CustomizableUI.removeListener(this);
   1.220 +
   1.221 +        assert.pass('button properly removed');
   1.222 +        loader.unload();
   1.223 +        done();
   1.224 +      }
   1.225 +    }
   1.226 +  });
   1.227 +
   1.228 +  let button = ActionButton({
   1.229 +    id: 'my-button-3',
   1.230 +    label: 'my button',
   1.231 +    icon: './icon.png'
   1.232 +  });
   1.233 +
   1.234 +  // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
   1.235 +  // was removed or it's not in the UX build yet
   1.236 +  widgetId = getWidget(button.id).id;
   1.237 +
   1.238 +  button.destroy();
   1.239 +};
   1.240 +
   1.241 +exports['test button global state updated'] = function(assert) {
   1.242 +  let loader = Loader(module);
   1.243 +  let { ActionButton } = loader.require('sdk/ui');
   1.244 +
   1.245 +  let button = ActionButton({
   1.246 +    id: 'my-button-4',
   1.247 +    label: 'my button',
   1.248 +    icon: './icon.png'
   1.249 +  });
   1.250 +
   1.251 +  // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
   1.252 +  // was removed or it's not in the UX build yet
   1.253 +
   1.254 +  let { node, id: widgetId } = getWidget(button.id);
   1.255 +
   1.256 +  // check read-only properties
   1.257 +
   1.258 +  assert.throws(() => button.id = 'another-id',
   1.259 +    /^setting a property that has only a getter/,
   1.260 +    'id cannot be set at runtime');
   1.261 +
   1.262 +  assert.equal(button.id, 'my-button-4',
   1.263 +    'id is unchanged');
   1.264 +  assert.equal(node.id, widgetId,
   1.265 +    'node id is unchanged');
   1.266 +
   1.267 +  // check writable properties
   1.268 +
   1.269 +  button.label = 'New label';
   1.270 +  assert.equal(button.label, 'New label',
   1.271 +    'label is updated');
   1.272 +  assert.equal(node.getAttribute('label'), 'New label',
   1.273 +    'node label is updated');
   1.274 +  assert.equal(node.getAttribute('tooltiptext'), 'New label',
   1.275 +    'node tooltip is updated');
   1.276 +
   1.277 +  button.icon = './new-icon.png';
   1.278 +  assert.equal(button.icon, './new-icon.png',
   1.279 +    'icon is updated');
   1.280 +  assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
   1.281 +    'node image is updated');
   1.282 +
   1.283 +  button.disabled = true;
   1.284 +  assert.equal(button.disabled, true,
   1.285 +    'disabled is updated');
   1.286 +  assert.equal(node.getAttribute('disabled'), 'true',
   1.287 +    'node disabled is updated');
   1.288 +
   1.289 +  // TODO: test validation on update
   1.290 +
   1.291 +  loader.unload();
   1.292 +}
   1.293 +
   1.294 +exports['test button global state set and get with state method'] = function(assert) {
   1.295 +  let loader = Loader(module);
   1.296 +  let { ActionButton } = loader.require('sdk/ui');
   1.297 +
   1.298 +  let button = ActionButton({
   1.299 +    id: 'my-button-16',
   1.300 +    label: 'my button',
   1.301 +    icon: './icon.png'
   1.302 +  });
   1.303 +
   1.304 +  // read the button's state
   1.305 +  let state = button.state(button);
   1.306 +
   1.307 +  assert.equal(state.label, 'my button',
   1.308 +    'label is correct');
   1.309 +  assert.equal(state.icon, './icon.png',
   1.310 +    'icon is correct');
   1.311 +  assert.equal(state.disabled, false,
   1.312 +    'disabled is correct');
   1.313 +
   1.314 +  // set the new button's state
   1.315 +  button.state(button, {
   1.316 +    label: 'New label',
   1.317 +    icon: './new-icon.png',
   1.318 +    disabled: true
   1.319 +  });
   1.320 +
   1.321 +  assert.equal(button.label, 'New label',
   1.322 +    'label is updated');
   1.323 +  assert.equal(button.icon, './new-icon.png',
   1.324 +    'icon is updated');
   1.325 +  assert.equal(button.disabled, true,
   1.326 +    'disabled is updated');
   1.327 +
   1.328 +  loader.unload();
   1.329 +}
   1.330 +
   1.331 +exports['test button global state updated on multiple windows'] = function(assert, done) {
   1.332 +  let loader = Loader(module);
   1.333 +  let { ActionButton } = loader.require('sdk/ui');
   1.334 +
   1.335 +  let button = ActionButton({
   1.336 +    id: 'my-button-5',
   1.337 +    label: 'my button',
   1.338 +    icon: './icon.png'
   1.339 +  });
   1.340 +
   1.341 +  let nodes = [getWidget(button.id).node];
   1.342 +
   1.343 +  openBrowserWindow().then(window => {
   1.344 +    nodes.push(getWidget(button.id, window).node);
   1.345 +
   1.346 +    button.label = 'New label';
   1.347 +    button.icon = './new-icon.png';
   1.348 +    button.disabled = true;
   1.349 +
   1.350 +    for (let node of nodes) {
   1.351 +      assert.equal(node.getAttribute('label'), 'New label',
   1.352 +        'node label is updated');
   1.353 +      assert.equal(node.getAttribute('tooltiptext'), 'New label',
   1.354 +        'node tooltip is updated');
   1.355 +
   1.356 +      assert.equal(button.icon, './new-icon.png',
   1.357 +        'icon is updated');
   1.358 +      assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
   1.359 +        'node image is updated');
   1.360 +
   1.361 +      assert.equal(button.disabled, true,
   1.362 +        'disabled is updated');
   1.363 +      assert.equal(node.getAttribute('disabled'), 'true',
   1.364 +        'node disabled is updated');
   1.365 +    };
   1.366 +
   1.367 +    return window;
   1.368 +  }).
   1.369 +  then(close).
   1.370 +  then(loader.unload).
   1.371 +  then(done, assert.fail);
   1.372 +};
   1.373 +
   1.374 +exports['test button window state'] = function(assert, done) {
   1.375 +  let loader = Loader(module);
   1.376 +  let { ActionButton } = loader.require('sdk/ui');
   1.377 +  let { browserWindows } = loader.require('sdk/windows');
   1.378 +
   1.379 +  let button = ActionButton({
   1.380 +    id: 'my-button-6',
   1.381 +    label: 'my button',
   1.382 +    icon: './icon.png'
   1.383 +  });
   1.384 +
   1.385 +  let mainWindow = browserWindows.activeWindow;
   1.386 +  let nodes = [getWidget(button.id).node];
   1.387 +
   1.388 +  openBrowserWindow().then(focus).then(window => {
   1.389 +    nodes.push(getWidget(button.id, window).node);
   1.390 +
   1.391 +    let { activeWindow } = browserWindows;
   1.392 +
   1.393 +    button.state(activeWindow, {
   1.394 +      label: 'New label',
   1.395 +      icon: './new-icon.png',
   1.396 +      disabled: true
   1.397 +    });
   1.398 +
   1.399 +    // check the states
   1.400 +
   1.401 +    assert.equal(button.label, 'my button',
   1.402 +      'global label unchanged');
   1.403 +    assert.equal(button.icon, './icon.png',
   1.404 +      'global icon unchanged');
   1.405 +    assert.equal(button.disabled, false,
   1.406 +      'global disabled unchanged');
   1.407 +
   1.408 +    let state = button.state(mainWindow);
   1.409 +
   1.410 +    assert.equal(state.label, 'my button',
   1.411 +      'previous window label unchanged');
   1.412 +    assert.equal(state.icon, './icon.png',
   1.413 +      'previous window icon unchanged');
   1.414 +    assert.equal(state.disabled, false,
   1.415 +      'previous window disabled unchanged');
   1.416 +
   1.417 +    let state = button.state(activeWindow);
   1.418 +
   1.419 +    assert.equal(state.label, 'New label',
   1.420 +      'active window label updated');
   1.421 +    assert.equal(state.icon, './new-icon.png',
   1.422 +      'active window icon updated');
   1.423 +    assert.equal(state.disabled, true,
   1.424 +      'active disabled updated');
   1.425 +
   1.426 +    // change the global state, only the windows without a state are affected
   1.427 +
   1.428 +    button.label = 'A good label';
   1.429 +
   1.430 +    assert.equal(button.label, 'A good label',
   1.431 +      'global label updated');
   1.432 +    assert.equal(button.state(mainWindow).label, 'A good label',
   1.433 +      'previous window label updated');
   1.434 +    assert.equal(button.state(activeWindow).label, 'New label',
   1.435 +      'active window label unchanged');
   1.436 +
   1.437 +    // delete the window state will inherits the global state again
   1.438 +
   1.439 +    button.state(activeWindow, null);
   1.440 +
   1.441 +    assert.equal(button.state(activeWindow).label, 'A good label',
   1.442 +      'active window label inherited');
   1.443 +
   1.444 +    // check the nodes properties
   1.445 +    let node = nodes[0];
   1.446 +    let state = button.state(mainWindow);
   1.447 +
   1.448 +    assert.equal(node.getAttribute('label'), state.label,
   1.449 +      'node label is correct');
   1.450 +    assert.equal(node.getAttribute('tooltiptext'), state.label,
   1.451 +      'node tooltip is correct');
   1.452 +
   1.453 +    assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
   1.454 +      'node image is correct');
   1.455 +    assert.equal(node.hasAttribute('disabled'), state.disabled,
   1.456 +      'disabled is correct');
   1.457 +
   1.458 +    let node = nodes[1];
   1.459 +    let state = button.state(activeWindow);
   1.460 +
   1.461 +    assert.equal(node.getAttribute('label'), state.label,
   1.462 +      'node label is correct');
   1.463 +    assert.equal(node.getAttribute('tooltiptext'), state.label,
   1.464 +      'node tooltip is correct');
   1.465 +
   1.466 +    assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
   1.467 +      'node image is correct');
   1.468 +    assert.equal(node.hasAttribute('disabled'), state.disabled,
   1.469 +      'disabled is correct');
   1.470 +
   1.471 +    return window;
   1.472 +  }).
   1.473 +  then(close).
   1.474 +  then(loader.unload).
   1.475 +  then(done, assert.fail);
   1.476 +};
   1.477 +
   1.478 +
   1.479 +exports['test button tab state'] = function(assert, done) {
   1.480 +  let loader = Loader(module);
   1.481 +  let { ActionButton } = loader.require('sdk/ui');
   1.482 +  let { browserWindows } = loader.require('sdk/windows');
   1.483 +  let tabs = loader.require('sdk/tabs');
   1.484 +
   1.485 +  let button = ActionButton({
   1.486 +    id: 'my-button-7',
   1.487 +    label: 'my button',
   1.488 +    icon: './icon.png'
   1.489 +  });
   1.490 +
   1.491 +  let mainTab = tabs.activeTab;
   1.492 +  let node = getWidget(button.id).node;
   1.493 +
   1.494 +  tabs.open({
   1.495 +    url: 'about:blank',
   1.496 +    onActivate: function onActivate(tab) {
   1.497 +      tab.removeListener('activate', onActivate);
   1.498 +
   1.499 +      let { activeWindow } = browserWindows;
   1.500 +      // set window state
   1.501 +      button.state(activeWindow, {
   1.502 +        label: 'Window label',
   1.503 +        icon: './window-icon.png'
   1.504 +      });
   1.505 +
   1.506 +      // set previous active tab state
   1.507 +      button.state(mainTab, {
   1.508 +        label: 'Tab label',
   1.509 +        icon: './tab-icon.png',
   1.510 +      });
   1.511 +
   1.512 +      // set current active tab state
   1.513 +      button.state(tab, {
   1.514 +        icon: './another-tab-icon.png',
   1.515 +        disabled: true
   1.516 +      });
   1.517 +
   1.518 +      // check the states
   1.519 +
   1.520 +      Cu.schedulePreciseGC(() => {
   1.521 +        assert.equal(button.label, 'my button',
   1.522 +          'global label unchanged');
   1.523 +        assert.equal(button.icon, './icon.png',
   1.524 +          'global icon unchanged');
   1.525 +        assert.equal(button.disabled, false,
   1.526 +          'global disabled unchanged');
   1.527 +
   1.528 +        let state = button.state(mainTab);
   1.529 +
   1.530 +        assert.equal(state.label, 'Tab label',
   1.531 +          'previous tab label updated');
   1.532 +        assert.equal(state.icon, './tab-icon.png',
   1.533 +          'previous tab icon updated');
   1.534 +        assert.equal(state.disabled, false,
   1.535 +          'previous tab disabled unchanged');
   1.536 +
   1.537 +        let state = button.state(tab);
   1.538 +
   1.539 +        assert.equal(state.label, 'Window label',
   1.540 +          'active tab inherited from window state');
   1.541 +        assert.equal(state.icon, './another-tab-icon.png',
   1.542 +          'active tab icon updated');
   1.543 +        assert.equal(state.disabled, true,
   1.544 +          'active disabled updated');
   1.545 +
   1.546 +        // change the global state
   1.547 +        button.icon = './good-icon.png';
   1.548 +
   1.549 +        // delete the tab state
   1.550 +        button.state(tab, null);
   1.551 +
   1.552 +        assert.equal(button.icon, './good-icon.png',
   1.553 +          'global icon updated');
   1.554 +        assert.equal(button.state(mainTab).icon, './tab-icon.png',
   1.555 +          'previous tab icon unchanged');
   1.556 +        assert.equal(button.state(tab).icon, './window-icon.png',
   1.557 +          'tab icon inherited from window');
   1.558 +
   1.559 +        // delete the window state
   1.560 +        button.state(activeWindow, null);
   1.561 +
   1.562 +        assert.equal(button.state(tab).icon, './good-icon.png',
   1.563 +          'tab icon inherited from global');
   1.564 +
   1.565 +        // check the node properties
   1.566 +
   1.567 +        let state = button.state(tabs.activeTab);
   1.568 +
   1.569 +        assert.equal(node.getAttribute('label'), state.label,
   1.570 +          'node label is correct');
   1.571 +        assert.equal(node.getAttribute('tooltiptext'), state.label,
   1.572 +          'node tooltip is correct');
   1.573 +        assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
   1.574 +          'node image is correct');
   1.575 +        assert.equal(node.hasAttribute('disabled'), state.disabled,
   1.576 +          'disabled is correct');
   1.577 +
   1.578 +        tabs.once('activate', () => {
   1.579 +          // This is made in order to avoid to check the node before it
   1.580 +          // is updated, need a better check
   1.581 +          setTimeout(() => {
   1.582 +            let state = button.state(mainTab);
   1.583 +
   1.584 +            assert.equal(node.getAttribute('label'), state.label,
   1.585 +              'node label is correct');
   1.586 +            assert.equal(node.getAttribute('tooltiptext'), state.label,
   1.587 +              'node tooltip is correct');
   1.588 +            assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
   1.589 +              'node image is correct');
   1.590 +            assert.equal(node.hasAttribute('disabled'), state.disabled,
   1.591 +              'disabled is correct');
   1.592 +
   1.593 +            tab.close(() => {
   1.594 +              loader.unload();
   1.595 +              done();
   1.596 +            });
   1.597 +          }, 500);
   1.598 +        });
   1.599 +
   1.600 +        mainTab.activate();
   1.601 +      });
   1.602 +    }
   1.603 +  });
   1.604 +
   1.605 +};
   1.606 +
   1.607 +exports['test button click'] = function(assert, done) {
   1.608 +  let loader = Loader(module);
   1.609 +  let { ActionButton } = loader.require('sdk/ui');
   1.610 +  let { browserWindows } = loader.require('sdk/windows');
   1.611 +
   1.612 +  let labels = [];
   1.613 +
   1.614 +  let button = ActionButton({
   1.615 +    id: 'my-button-8',
   1.616 +    label: 'my button',
   1.617 +    icon: './icon.png',
   1.618 +    onClick: ({label}) => labels.push(label)
   1.619 +  });
   1.620 +
   1.621 +  let mainWindow = browserWindows.activeWindow;
   1.622 +  let chromeWindow = getMostRecentBrowserWindow();
   1.623 +
   1.624 +  openBrowserWindow().then(focus).then(window => {
   1.625 +    button.state(mainWindow, { label: 'nothing' });
   1.626 +    button.state(mainWindow.tabs.activeTab, { label: 'foo'})
   1.627 +    button.state(browserWindows.activeWindow, { label: 'bar' });
   1.628 +
   1.629 +    button.click();
   1.630 +
   1.631 +    focus(chromeWindow).then(() => {
   1.632 +      button.click();
   1.633 +
   1.634 +      assert.deepEqual(labels, ['bar', 'foo'],
   1.635 +        'button click works');
   1.636 +
   1.637 +      close(window).
   1.638 +        then(loader.unload).
   1.639 +        then(done, assert.fail);
   1.640 +    });
   1.641 +  }).then(null, assert.fail);
   1.642 +
   1.643 +}
   1.644 +
   1.645 +exports['test button icon set'] = function(assert) {
   1.646 +  const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
   1.647 +  let loader = Loader(module);
   1.648 +  let { ActionButton } = loader.require('sdk/ui');
   1.649 +
   1.650 +  // Test remote icon set
   1.651 +  assert.throws(
   1.652 +    () => ActionButton({
   1.653 +      id: 'my-button-10',
   1.654 +      label: 'my button',
   1.655 +      icon: {
   1.656 +        '16': 'http://www.mozilla.org/favicon.ico'
   1.657 +      }
   1.658 +    }),
   1.659 +    /^The option "icon"/,
   1.660 +    'throws on no valid icon given');
   1.661 +
   1.662 +  let button = ActionButton({
   1.663 +    id: 'my-button-11',
   1.664 +    label: 'my button',
   1.665 +    icon: {
   1.666 +      '5': './icon5.png',
   1.667 +      '16': './icon16.png',
   1.668 +      '32': './icon32.png',
   1.669 +      '64': './icon64.png'
   1.670 +    }
   1.671 +  });
   1.672 +
   1.673 +  let { node, id: widgetId } = getWidget(button.id);
   1.674 +  let { devicePixelRatio } = node.ownerDocument.defaultView;
   1.675 +
   1.676 +  let size = 16 * devicePixelRatio;
   1.677 +
   1.678 +  assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
   1.679 +    'the icon is set properly in navbar');
   1.680 +
   1.681 +  let size = 32 * devicePixelRatio;
   1.682 +
   1.683 +  CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
   1.684 +
   1.685 +  assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
   1.686 +    'the icon is set properly in panel');
   1.687 +
   1.688 +  // Using `loader.unload` without move back the button to the original area
   1.689 +  // raises an error in the CustomizableUI. This is doesn't happen if the
   1.690 +  // button is moved manually from navbar to panel. I believe it has to do
   1.691 +  // with `addWidgetToArea` method, because even with a `timeout` the issue
   1.692 +  // persist.
   1.693 +  CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
   1.694 +
   1.695 +  loader.unload();
   1.696 +}
   1.697 +
   1.698 +exports['test button icon se with only one option'] = function(assert) {
   1.699 +  const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
   1.700 +  let loader = Loader(module);
   1.701 +  let { ActionButton } = loader.require('sdk/ui');
   1.702 +
   1.703 +  // Test remote icon set
   1.704 +  assert.throws(
   1.705 +    () => ActionButton({
   1.706 +      id: 'my-button-10',
   1.707 +      label: 'my button',
   1.708 +      icon: {
   1.709 +        '16': 'http://www.mozilla.org/favicon.ico'
   1.710 +      }
   1.711 +    }),
   1.712 +    /^The option "icon"/,
   1.713 +    'throws on no valid icon given');
   1.714 +
   1.715 +  let button = ActionButton({
   1.716 +    id: 'my-button-11',
   1.717 +    label: 'my button',
   1.718 +    icon: {
   1.719 +      '5': './icon5.png'
   1.720 +    }
   1.721 +  });
   1.722 +
   1.723 +  let { node, id: widgetId } = getWidget(button.id);
   1.724 +
   1.725 +  assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
   1.726 +    'the icon is set properly in navbar');
   1.727 +
   1.728 +  CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
   1.729 +
   1.730 +  assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
   1.731 +    'the icon is set properly in panel');
   1.732 +
   1.733 +  // Using `loader.unload` without move back the button to the original area
   1.734 +  // raises an error in the CustomizableUI. This is doesn't happen if the
   1.735 +  // button is moved manually from navbar to panel. I believe it has to do
   1.736 +  // with `addWidgetToArea` method, because even with a `timeout` the issue
   1.737 +  // persist.
   1.738 +  CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
   1.739 +
   1.740 +  loader.unload();
   1.741 +}
   1.742 +
   1.743 +exports['test button state validation'] = function(assert) {
   1.744 +  let loader = Loader(module);
   1.745 +  let { ActionButton } = loader.require('sdk/ui');
   1.746 +  let { browserWindows } = loader.require('sdk/windows');
   1.747 +
   1.748 +  let button = ActionButton({
   1.749 +    id: 'my-button-12',
   1.750 +    label: 'my button',
   1.751 +    icon: './icon.png'
   1.752 +  })
   1.753 +
   1.754 +  let state = button.state(button);
   1.755 +
   1.756 +  assert.throws(
   1.757 +    () => button.state(button, { icon: 'http://www.mozilla.org/favicon.ico' }),
   1.758 +    /^The option "icon"/,
   1.759 +    'throws on remote icon given');
   1.760 +
   1.761 +  loader.unload();
   1.762 +};
   1.763 +
   1.764 +exports['test button are not in private windows'] = function(assert, done) {
   1.765 +  let loader = Loader(module);
   1.766 +  let { ActionButton } = loader.require('sdk/ui');
   1.767 +  let{ isPrivate } = loader.require('sdk/private-browsing');
   1.768 +  let { browserWindows } = loader.require('sdk/windows');
   1.769 +
   1.770 +  let button = ActionButton({
   1.771 +    id: 'my-button-13',
   1.772 +    label: 'my button',
   1.773 +    icon: './icon.png'
   1.774 +  });
   1.775 +
   1.776 +  openPrivateBrowserWindow().then(window => {
   1.777 +    assert.ok(isPrivate(window),
   1.778 +      'the new window is private');
   1.779 +
   1.780 +    let { node } = getWidget(button.id, window);
   1.781 +
   1.782 +    assert.ok(!node || node.style.display === 'none',
   1.783 +      'the button is not added / is not visible on private window');
   1.784 +
   1.785 +    return window;
   1.786 +  }).
   1.787 +  then(close).
   1.788 +  then(loader.unload).
   1.789 +  then(done, assert.fail)
   1.790 +}
   1.791 +
   1.792 +exports['test button state are snapshot'] = function(assert) {
   1.793 +  let loader = Loader(module);
   1.794 +  let { ActionButton } = loader.require('sdk/ui');
   1.795 +  let { browserWindows } = loader.require('sdk/windows');
   1.796 +  let tabs = loader.require('sdk/tabs');
   1.797 +
   1.798 +  let button = ActionButton({
   1.799 +    id: 'my-button-14',
   1.800 +    label: 'my button',
   1.801 +    icon: './icon.png'
   1.802 +  });
   1.803 +
   1.804 +  let state = button.state(button);
   1.805 +  let windowState = button.state(browserWindows.activeWindow);
   1.806 +  let tabState = button.state(tabs.activeTab);
   1.807 +
   1.808 +  assert.deepEqual(windowState, state,
   1.809 +    'window state has the same properties of button state');
   1.810 +
   1.811 +  assert.deepEqual(tabState, state,
   1.812 +    'tab state has the same properties of button state');
   1.813 +
   1.814 +  assert.notEqual(windowState, state,
   1.815 +    'window state is not the same object of button state');
   1.816 +
   1.817 +  assert.notEqual(tabState, state,
   1.818 +    'tab state is not the same object of button state');
   1.819 +
   1.820 +  assert.deepEqual(button.state(button), state,
   1.821 +    'button state has the same content of previous button state');
   1.822 +
   1.823 +  assert.deepEqual(button.state(browserWindows.activeWindow), windowState,
   1.824 +    'window state has the same content of previous window state');
   1.825 +
   1.826 +  assert.deepEqual(button.state(tabs.activeTab), tabState,
   1.827 +    'tab state has the same content of previous tab state');
   1.828 +
   1.829 +  assert.notEqual(button.state(button), state,
   1.830 +    'button state is not the same object of previous button state');
   1.831 +
   1.832 +  assert.notEqual(button.state(browserWindows.activeWindow), windowState,
   1.833 +    'window state is not the same object of previous window state');
   1.834 +
   1.835 +  assert.notEqual(button.state(tabs.activeTab), tabState,
   1.836 +    'tab state is not the same object of previous tab state');
   1.837 +
   1.838 +  loader.unload();
   1.839 +}
   1.840 +
   1.841 +exports['test button icon object is a snapshot'] = function(assert) {
   1.842 +  let loader = Loader(module);
   1.843 +  let { ActionButton } = loader.require('sdk/ui');
   1.844 +
   1.845 +  let icon = {
   1.846 +    '16': './foo.png'
   1.847 +  };
   1.848 +
   1.849 +  let button = ActionButton({
   1.850 +    id: 'my-button-17',
   1.851 +    label: 'my button',
   1.852 +    icon: icon
   1.853 +  });
   1.854 +
   1.855 +  assert.deepEqual(button.icon, icon,
   1.856 +    'button.icon has the same properties of the object set in the constructor');
   1.857 +
   1.858 +  assert.notEqual(button.icon, icon,
   1.859 +    'button.icon is not the same object of the object set in the constructor');
   1.860 +
   1.861 +  assert.throws(
   1.862 +    () => button.icon[16] = './bar.png',
   1.863 +    /16 is read-only/,
   1.864 +    'properties of button.icon are ready-only'
   1.865 +  );
   1.866 +
   1.867 +  let newIcon = {'16': './bar.png'};
   1.868 +  button.icon = newIcon;
   1.869 +
   1.870 +  assert.deepEqual(button.icon, newIcon,
   1.871 +    'button.icon has the same properties of the object set');
   1.872 +
   1.873 +  assert.notEqual(button.icon, newIcon,
   1.874 +    'button.icon is not the same object of the object set');
   1.875 +
   1.876 +  loader.unload();
   1.877 +}
   1.878 +
   1.879 +exports['test button after destroy'] = function(assert) {
   1.880 +  let loader = Loader(module);
   1.881 +  let { ActionButton } = loader.require('sdk/ui');
   1.882 +  let { browserWindows } = loader.require('sdk/windows');
   1.883 +  let { activeTab } = loader.require('sdk/tabs');
   1.884 +
   1.885 +  let button = ActionButton({
   1.886 +    id: 'my-button-15',
   1.887 +    label: 'my button',
   1.888 +    icon: './icon.png',
   1.889 +    onClick: () => assert.fail('onClick should not be called')
   1.890 +  });
   1.891 +
   1.892 +  button.destroy();
   1.893 +
   1.894 +  assert.throws(
   1.895 +    () => button.click(),
   1.896 +    /^The state cannot be set or get/,
   1.897 +    'button.click() not executed');
   1.898 +
   1.899 +  assert.throws(
   1.900 +    () => button.label,
   1.901 +    /^The state cannot be set or get/,
   1.902 +    'button.label cannot be get after destroy');
   1.903 +
   1.904 +  assert.throws(
   1.905 +    () => button.label = 'my label',
   1.906 +    /^The state cannot be set or get/,
   1.907 +    'button.label cannot be set after destroy');
   1.908 +
   1.909 +  assert.throws(
   1.910 +    () => {
   1.911 +      button.state(browserWindows.activeWindow, {
   1.912 +        label: 'window label'
   1.913 +      });
   1.914 +    },
   1.915 +    /^The state cannot be set or get/,
   1.916 +    'window state label cannot be set after destroy');
   1.917 +
   1.918 +  assert.throws(
   1.919 +    () => button.state(browserWindows.activeWindow).label,
   1.920 +    /^The state cannot be set or get/,
   1.921 +    'window state label cannot be get after destroy');
   1.922 +
   1.923 +  assert.throws(
   1.924 +    () => {
   1.925 +      button.state(activeTab, {
   1.926 +        label: 'tab label'
   1.927 +      });
   1.928 +    },
   1.929 +    /^The state cannot be set or get/,
   1.930 +    'tab state label cannot be set after destroy');
   1.931 +
   1.932 +  assert.throws(
   1.933 +    () => button.state(activeTab).label,
   1.934 +    /^The state cannot be set or get/,
   1.935 +    'window state label cannot se get after destroy');
   1.936 +
   1.937 +  loader.unload();
   1.938 +};
   1.939 +
   1.940 +require('sdk/test').run(exports);

mercurial