bluetooth_options_browsertest.js revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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
5GEN('#if defined(OS_CHROMEOS)');
6
7function BluetoothWebUITest() {}
8
9BluetoothWebUITest.prototype = {
10  __proto__: testing.Test.prototype,
11
12  /**
13   * Start tests from the main-settings page.
14   */
15  browsePreload: 'chrome://settings-frame/settings',
16
17  /**
18   * @inheritDoc
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
71TEST_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
85TEST_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
169TEST_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
227TEST_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
298TEST_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
374GEN('#endif');
375