Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 GEN('#if defined(OS_CHROMEOS)');
      6 
      7 function BluetoothWebUITest() {}
      8 
      9 BluetoothWebUITest.prototype = {
     10   __proto__: testing.Test.prototype,
     11 
     12   /**
     13    * Start tests from the main-settings page.
     14    */
     15   browsePreload: 'chrome://settings-frame/',
     16 
     17   /**
     18    * @override
     19    */
     20   preLoad: function() {
     21     this.makeAndRegisterMockHandler([
     22       'bluetoothEnableChange',
     23       'updateBluetoothDevice',
     24       'findBluetoothDevices',
     25       'stopBluetoothDeviceDiscovery',
     26     ]);
     27   },
     28 
     29   /**
     30    * Retrieves the list item associated with a Bluetooth device.
     31    * @param {!Element} listElement Element containing a list of devices.
     32    * @param {string} deviceName The name of the device.
     33    * @return {Element|undefined} List item matching the device name.
     34    */
     35   getElementForDevice: function(listElement, deviceName) {
     36     var items = listElement.querySelectorAll('.bluetooth-device');
     37     for (var i = 0; i < items.length; i++) {
     38       var candidate = items[i];
     39       var name = candidate.data.name;
     40       if (name == deviceName)
     41         return candidate;
     42     }
     43   },
     44 
     45   /**
     46    * Selects a bluetooth device from the list with the matching address.
     47    * @param {!Element} listElement A list of Bluetooth devices.
     48    * @param {{address: string,
     49    *          connectable: boolean,
     50    *          connected: boolean,
     51    *          name: string,
     52    *          paired: boolean}} device Description of the device.
     53    */
     54   selectDevice: function(listElement, device) {
     55     listElement.selectedItem = device;
     56     cr.dispatchSimpleEvent(listElement, 'change');
     57   },
     58 
     59   /**
     60    * Fake input of a pincode or passkey.
     61    * @param {!Element} element Text input field.
     62    * @param {string} text New value for the input field.
     63    */
     64   fakeInput: function(element, text) {
     65     element.value = text;
     66     cr.dispatchSimpleEvent(element, 'input');
     67   },
     68 
     69 };
     70 
     71 TEST_F('BluetoothWebUITest', 'testEnableBluetooth', function() {
     72   assertEquals(this.browsePreload, document.location.href);
     73   expectFalse($('enable-bluetooth').checked);
     74   expectTrue($('bluetooth-paired-devices-list').parentNode.hidden);
     75 
     76   this.mockHandler.expects(once()).bluetoothEnableChange([true]).will(
     77       callFunction(function() {
     78         options.BrowserOptions.setBluetoothState(true);
     79       }));
     80   $('enable-bluetooth').click();
     81 
     82   expectFalse($('bluetooth-paired-devices-list').parentNode.hidden);
     83 });
     84 
     85 TEST_F('BluetoothWebUITest', 'testAddDevices', function() {
     86   assertEquals(this.browsePreload, document.location.href);
     87 
     88   var pairedDeviceList = $('bluetooth-paired-devices-list');
     89   var unpairedDeviceList = $('bluetooth-unpaired-devices-list');
     90 
     91   var fakePairedDevice = {
     92     address: '00:11:22:33:44:55',
     93     connectable: true,
     94     connected: false,
     95     name: 'Fake device',
     96     paired: true
     97   };
     98 
     99   var fakeUnpairedDevice = {
    100     address: '28:CF:DA:00:00:00',
    101     connectable: true,
    102     connected: false,
    103     name: 'Apple Magic Mouse',
    104     paired: false
    105   };
    106 
    107   var fakeUnpairedDevice2 = {
    108     address: '28:37:37:00:00:00',
    109     connectable: true,
    110     connected: false,
    111     name: 'Apple Wireless Keyboard',
    112     paired: false
    113   };
    114 
    115   // Ensure data models for the paired and unpaired device lists are properly
    116   // updated.
    117   var index = pairedDeviceList.find(fakePairedDevice.address);
    118   expectEquals(undefined, index);
    119   options.BrowserOptions.addBluetoothDevice(fakePairedDevice);
    120   index = pairedDeviceList.find(fakePairedDevice.address);
    121   expectEquals(0, index);
    122 
    123   // Ensure the DOM contents of the list are properly updated. The default
    124   // layout of a list creates DOM elements only for visible elements in the
    125   // list, which is problematic if the list is hidden at the time of layout.
    126   // The Bluetooth device lists overcomes this problem by using fixed sized
    127   // elements in an auto-expanding list. This test ensures the problem stays
    128   // fixed.
    129   expectTrue(!!this.getElementForDevice(pairedDeviceList,
    130                                         fakePairedDevice.name));
    131   expectFalse(!!this.getElementForDevice(unpairedDeviceList,
    132                                          fakePairedDevice.name));
    133 
    134   options.BrowserOptions.addBluetoothDevice(fakeUnpairedDevice);
    135   index = unpairedDeviceList.find(fakeUnpairedDevice.address);
    136   expectEquals(0, index);
    137   expectFalse(!!this.getElementForDevice(pairedDeviceList,
    138                                          fakeUnpairedDevice.name));
    139   expectTrue(!!this.getElementForDevice(unpairedDeviceList,
    140                                         fakeUnpairedDevice.name));
    141 
    142   // Test adding a second device to a list.
    143   options.BrowserOptions.addBluetoothDevice(fakeUnpairedDevice2);
    144   index = unpairedDeviceList.find(fakeUnpairedDevice2.address);
    145   expectEquals(1, index);
    146   expectTrue(!!this.getElementForDevice(unpairedDeviceList,
    147                                         fakeUnpairedDevice2.name));
    148 
    149   // Test clicking on the 'Add a device' button.
    150   this.mockHandler.expects(once()).findBluetoothDevices();
    151   $('bluetooth-add-device').click();
    152   expectFalse($('bluetooth-options').hidden);
    153   expectTrue($('bluetooth-add-device-apply-button').disabled);
    154   expectFalse($('bluetooth-add-device-cancel-button').disabled);
    155 
    156   Mock4JS.verifyAllMocks();
    157   Mock4JS.clearMocksToVerify();
    158 
    159   // Test selecting an element and clicking on the connect button.
    160   this.mockHandler.expects(once()).stopBluetoothDeviceDiscovery();
    161   this.mockHandler.expects(once()).updateBluetoothDevice(
    162       [fakeUnpairedDevice2.address, 'connect']);
    163   this.selectDevice(unpairedDeviceList, fakeUnpairedDevice2);
    164   var connectButton = $('bluetooth-add-device-apply-button');
    165   expectFalse(connectButton.disabled);
    166   connectButton.click();
    167 });
    168 
    169 TEST_F('BluetoothWebUITest', 'testDevicePairing', function() {
    170   assertEquals(this.browsePreload, document.location.href);
    171 
    172   var pairedDeviceList = $('bluetooth-paired-devices-list');
    173   var unpairedDeviceList = $('bluetooth-unpaired-devices-list');
    174 
    175   var fakeDevice = {
    176     address: '00:24:BE:00:00:00',
    177     connectable: true,
    178     connected: false,
    179     name: 'Sony BT-00',
    180     paired: false
    181   };
    182 
    183   options.BrowserOptions.addBluetoothDevice(fakeDevice);
    184   var index = unpairedDeviceList.find(fakeDevice.address);
    185   expectEquals(0, index);
    186   expectTrue(!!this.getElementForDevice(unpairedDeviceList,
    187                                         fakeDevice.name));
    188 
    189   // Simulate process of pairing a device.
    190   fakeDevice.pairing = 'bluetoothEnterPinCode';
    191   options.BrowserOptions.addBluetoothDevice(fakeDevice);
    192 
    193   // Verify that the pairing dialog is displayed with the proper options.
    194   expectFalse($('bluetooth-pairing').hidden);
    195   expectTrue($('bluetooth-pairing-passkey-display').hidden);
    196   expectTrue($('bluetooth-pairing-passkey-entry').hidden);
    197   expectFalse($('bluetooth-pairing-pincode-entry').hidden);
    198 
    199   // Connect button should be visible but disabled until a pincode is entered.
    200   expectFalse($('bluetooth-pair-device-connect-button').hidden);
    201   expectFalse($('bluetooth-pair-device-cancel-button').hidden);
    202   expectTrue($('bluetooth-pair-device-connect-button').disabled);
    203   expectFalse($('bluetooth-pair-device-cancel-button').disabled);
    204 
    205   // Simulate process of entering a pincode.
    206   var pincode = '123456';
    207 
    208   this.mockHandler.expects(once()).updateBluetoothDevice(
    209       [fakeDevice.address, 'connect', pincode]).will(
    210       callFunction(function() {
    211         delete fakeDevice.pairing;
    212         fakeDevice.paired = true;
    213         options.BrowserOptions.addBluetoothDevice(fakeDevice);
    214       }));
    215 
    216   this.fakeInput($('bluetooth-pincode'), pincode);
    217   $('bluetooth-pair-device-connect-button').click();
    218 
    219   // Verify that the device is removed from the unparied list and added to the
    220   // paired device list.
    221   expectTrue(!!this.getElementForDevice(pairedDeviceList,
    222                                         fakeDevice.name));
    223   expectFalse(!!this.getElementForDevice(unpairedDeviceList,
    224                                          fakeDevice.name));
    225 });
    226 
    227 TEST_F('BluetoothWebUITest', 'testConnectionState', function() {
    228   assertEquals(this.browsePreload, document.location.href);
    229 
    230   var pairedDeviceList = $('bluetooth-paired-devices-list');
    231   var connectButton = $('bluetooth-reconnect-device');
    232 
    233   var fakeDevice = {
    234     address: '00:24:BE:00:00:00',
    235     connectable: true,
    236     connected: false,
    237     name: 'Sony BT-00',
    238     paired: true
    239   };
    240 
    241   options.BrowserOptions.addBluetoothDevice(fakeDevice);
    242   var element = this.getElementForDevice(pairedDeviceList,
    243                                          fakeDevice.name);
    244   assertTrue(!!element);
    245   expectFalse(!!element.getAttribute('connected'));
    246   expectTrue(connectButton.disabled);
    247 
    248   // Simulate connecting to a previously paired device.
    249   this.selectDevice(pairedDeviceList, fakeDevice);
    250   expectFalse(connectButton.disabled);
    251   this.mockHandler.expects(once()).updateBluetoothDevice(
    252       [fakeDevice.address, 'connect']).will(
    253       callFunction(function() {
    254         fakeDevice.connected = true;
    255         options.BrowserOptions.addBluetoothDevice(fakeDevice);
    256       }));
    257   connectButton.click();
    258   element = this.getElementForDevice(pairedDeviceList,
    259                                      fakeDevice.name);
    260   assertTrue(!!element);
    261   expectTrue(!!element.getAttribute('connected'));
    262   var button = element.querySelector('.row-delete-button');
    263   expectTrue(!!button);
    264 
    265   Mock4JS.verifyAllMocks();
    266   Mock4JS.clearMocksToVerify();
    267 
    268   // Test disconnecting.
    269   this.mockHandler.expects(once()).updateBluetoothDevice(
    270       [fakeDevice.address, 'disconnect']).will(
    271       callFunction(function() {
    272         fakeDevice.connected = false;
    273         options.BrowserOptions.addBluetoothDevice(fakeDevice);
    274       }));
    275   button.click();
    276   element = this.getElementForDevice(pairedDeviceList,
    277                                      fakeDevice.name);
    278   assertTrue(!!element);
    279   expectFalse(!!element.getAttribute('connected'));
    280   button = element.querySelector('.row-delete-button');
    281   expectTrue(!!button);
    282 
    283   Mock4JS.verifyAllMocks();
    284   Mock4JS.clearMocksToVerify();
    285 
    286   // Test forgetting  a disconnected device.
    287   this.mockHandler.expects(once()).updateBluetoothDevice(
    288       [fakeDevice.address, 'forget']).will(
    289       callFunction(function() {
    290         options.BrowserOptions.removeBluetoothDevice(fakeDevice.address);
    291       }));
    292   button.click();
    293   expectFalse(!!this.getElementForDevice(pairedDeviceList,
    294                                          fakeDevice.name));
    295 });
    296 
    297 
    298 TEST_F('BluetoothWebUITest', 'testMaliciousInput', function() {
    299   assertEquals(this.browsePreload, document.location.href);
    300 
    301   var unpairedDeviceList = $('bluetooth-unpaired-devices-list');
    302   var pairDeviceDialog = $('bluetooth-pairing');
    303 
    304   var maliciousStrings = [
    305       '<SCRIPT>alert(1)</SCRIPT>',
    306       '>\'>\\"><SCRIPT>alert(1)</SCRIPT>',
    307       '<IMG SRC=\\"javascript:alert(1)\\">',
    308       '<A HREF=\\"data:text/html;base64,' +
    309           'PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo=\\">...</A>',
    310       '<div>',
    311       '<textarea>',
    312       '<style>',
    313       '[0xC0][0xBC]SCRIPT[0xC0][0xBE]alert(1)[0xC0][0xBC]/SCRIPT[0xC0][0xBE]',
    314       '+ADw-SCRIPT+AD4-alert(1)+ADw-/SCRIPT+AD4-',
    315       '&#<script>alert(1)</script>;',
    316       '<!-- Hello -- world > <SCRIPT>alert(1)</SCRIPT> -->',
    317       '<!<!-- Hello world > <SCRIPT>alert(1)</SCRIPT> -->',
    318       '\x3CSCRIPT\x3Ealert(1)\x3C/SCRIPT\x3E',
    319       '<IMG SRC=\\"j[0x00]avascript:alert(1)\\">',
    320       '<BASE HREF=\\"javascript:1;/**/\\"><IMG SRC=\\"alert(1)\\">',
    321       'javascript:alert(1);',
    322       ' xss_injection=\\"\\" ',
    323       '\\" xss_injection=\\"',
    324       '\' xss_injection=\'',
    325       '<!--',
    326       '\'',
    327       '\\"'
    328   ];
    329 
    330   var fakeDevice = {
    331     address: '11:22:33:44:55:66',
    332     connectable: true,
    333     connected: false,
    334     name: 'fake',
    335     paired: false,
    336     pairing: 'bluetoothStartConnecting'
    337   };
    338 
    339   options.BrowserOptions.addBluetoothDevice(fakeDevice);
    340 
    341   var nodeCount = function(node) {
    342     if (node.getAttribute)
    343       assertFalse(!!node.getAttribute('xss_injection'));
    344     var length = node.childNodes.length;
    345     var tally = length;
    346     for (var i = 0; i < length; i++) {
    347       tally += nodeCount(node.childNodes[i]);
    348     }
    349     return tally;
    350   };
    351 
    352   // Determine the expected sizes.
    353   var unpairedDeviceListSize = nodeCount(unpairedDeviceList);
    354   var pairDeviceDialogSize = nodeCount(pairDeviceDialog);
    355 
    356   // Ensure that updating the device with a malicious name does not corrupt the
    357   // structure of the document.  Tests the unpaired device list and bluetooth
    358   // pairing dialog.
    359   for (var i = 0; i < maliciousStrings.length; i++) {
    360     fakeDevice.name = maliciousStrings[i];
    361     options.BrowserOptions.addBluetoothDevice(fakeDevice);
    362     assertEquals(unpairedDeviceListSize, nodeCount(unpairedDeviceList));
    363     var element = this.getElementForDevice(unpairedDeviceList,
    364                                            fakeDevice.name);
    365     assertTrue(!!element);
    366     var label = element.querySelector('.bluetooth-device-label');
    367     assertTrue(!!label);
    368     assertEquals(maliciousStrings[i], label.textContent);
    369     assertEquals(pairDeviceDialogSize, nodeCount(pairDeviceDialog));
    370   }
    371 
    372 });
    373 
    374 GEN('#endif');
    375