1 // Copyright 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 /** 6 * Test fixture for sync internals WebUI testing. 7 * @constructor 8 * @extends {testing.Test} 9 */ 10 function SyncInternalsWebUITest() {} 11 12 SyncInternalsWebUITest.prototype = { 13 __proto__: testing.Test.prototype, 14 15 /** 16 * Browse to the sync internals page. 17 * @override 18 */ 19 browsePreload: 'chrome://sync-internals', 20 21 /** 22 * Disable accessibility testing for this page. 23 * @override 24 */ 25 runAccessibilityChecks: false, 26 27 /** @override */ 28 preLoad: function() { 29 this.makeAndRegisterMockHandler([ 30 'getAllNodes', 31 ]); 32 }, 33 34 /** 35 * Checks aboutInfo's details section for the specified field. 36 * @param {boolean} isValid Whether the field is valid. 37 * @param {string} key The name of the key to search for in details. 38 * @param {string} value The expected value if |key| is found. 39 * @return {boolean} whether the field was found in the details. 40 * @protected 41 */ 42 hasInDetails: function(isValid, key, value) { 43 var details = chrome.sync.aboutInfo.details; 44 if (!details) 45 return false; 46 for (var i = 0; i < details.length; ++i) { 47 if (!details[i].data) 48 continue; 49 for (var j = 0; j < details[i].data.length; ++j) { 50 var obj = details[i].data[j]; 51 if (obj.stat_name == key) 52 return obj.is_valid == isValid && obj.stat_value == value; 53 } 54 } 55 return false; 56 } 57 }; 58 59 /** 60 * Constant hard-coded value to return from mock getAllNodes. 61 * @const 62 */ 63 var HARD_CODED_ALL_NODES = [{ 64 'nodes': [{ 65 'ATTACHMENT_METADATA': '', 66 'BASE_SERVER_SPECIFICS': {}, 67 'BASE_VERSION': '1396470970810000', 68 'CTIME': 'Wednesday, December 31, 1969 4:00:00 PM', 69 'ID': 'sZ:ADqtAZwzF4GOIyvkI2enSI62AU5p/7MNmvuSSyf7yXJ1SkJwpp1YL' + 70 '6bbMkF8inzqW+EO6n2aPJ/uXccW9GHxorBlnKoZAWHVzg==', 71 'IS_DEL': false, 72 'IS_DIR': true, 73 'IS_UNAPPLIED_UPDATE': false, 74 'IS_UNSYNCED': false, 75 'LOCAL_EXTERNAL_ID': '0', 76 'META_HANDLE': '387', 77 'MTIME': 'Wednesday, December 31, 1969 4:00:00 PM', 78 'NON_UNIQUE_NAME': 'Autofill', 79 'PARENT_ID': 'r', 80 'SERVER_CTIME': 'Wednesday, December 31, 1969 4:00:00 PM', 81 'SERVER_IS_DEL': false, 82 'SERVER_IS_DIR': true, 83 'SERVER_MTIME': 'Wednesday, December 31, 1969 4:00:00 PM', 84 'SERVER_NON_UNIQUE_NAME': 'Autofill', 85 'SERVER_PARENT_ID': 'r', 86 'SERVER_SPECIFICS': { 87 'autofill': { 88 'usage_timestamp': [] 89 } 90 }, 91 'SERVER_UNIQUE_POSITION': 'INVALID[]', 92 'SERVER_VERSION': '1396470970810000', 93 'SPECIFICS': { 94 'autofill': { 95 'usage_timestamp': [] 96 } 97 }, 98 'SYNCING': false, 99 'TRANSACTION_VERSION': '1', 100 'UNIQUE_BOOKMARK_TAG': '', 101 'UNIQUE_CLIENT_TAG': '', 102 'UNIQUE_POSITION': 'INVALID[]', 103 'UNIQUE_SERVER_TAG': 'google_chrome_autofill', 104 'isDirty': false, 105 'serverModelType': 'Autofill' 106 }, { 107 'ATTACHMENT_METADATA': '', 108 'BASE_SERVER_SPECIFICS': {}, 109 'BASE_VERSION': '1394241139528639', 110 'CTIME': 'Friday, March 7, 2014 5:12:19 PM', 111 'ID': 'sZ:ADqtAZwzc/ol1iaz+yNLjjWak9PBE0o/hATzpqJsyq/HX2xzV2f88' + 112 'FaOrT7HDE4tyn7zx2LWgkAFvZfCA5mOy4p0XFgiY0L+mw==', 113 'IS_DEL': false, 114 'IS_DIR': false, 115 'IS_UNAPPLIED_UPDATE': false, 116 'IS_UNSYNCED': false, 117 'LOCAL_EXTERNAL_ID': '0', 118 'META_HANDLE': '2989', 119 'MTIME': 'Friday, March 7, 2014 5:12:19 PM', 120 'NON_UNIQUE_NAME': 'autofill_entry|Email|rlsynctet2', 121 'PARENT_ID': 'sZ:ADqtAZwzF4GOIyvkI2enSI62AU5p/7MNmvuSSyf7yXJ1Sk' + 122 'Jwpp1YL6bbMkF8inzqW+EO6n2aPJ/uXccW9GHxorBlnKoZAWHVzg==', 123 'SERVER_CTIME': 'Friday, March 7, 2014 5:12:19 PM', 124 'SERVER_IS_DEL': false, 125 'SERVER_IS_DIR': false, 126 'SERVER_MTIME': 'Friday, March 7, 2014 5:12:19 PM', 127 'SERVER_NON_UNIQUE_NAME': 'autofill_entry|Email|rlsynctet2', 128 'SERVER_PARENT_ID': 'sZ:ADqtAZwzF4GOIyvkI2enSI62AU5p/7MNmvuSSyf' + 129 '7yXJ1SkJwpp1YL6bbMkF8inzqW+EO6n2aPJ/uXccW9GHxorBlnKoZAWHVzg==', 130 'SERVER_SPECIFICS': { 131 'autofill': { 132 'name': 'Email', 133 'usage_timestamp': ['13038713887000000', '13038713890000000'], 134 'value': 'rlsynctet2' 135 } 136 }, 137 'SERVER_UNIQUE_POSITION': 'INVALID[]', 138 'SERVER_VERSION': '1394241139528639', 139 'SPECIFICS': { 140 'autofill': { 141 'name': 'Email', 142 'usage_timestamp': ['13038713887000000', '13038713890000000'], 143 'value': 'rlsynctet2' 144 } 145 }, 146 'SYNCING': false, 147 'TRANSACTION_VERSION': '1', 148 'UNIQUE_BOOKMARK_TAG': '', 149 'UNIQUE_CLIENT_TAG': 'EvliorKUf1rLjT+BGkNZp586Tsk=', 150 'UNIQUE_POSITION': 'INVALID[]', 151 'UNIQUE_SERVER_TAG': '', 152 'isDirty': false, 153 'serverModelType': 'Autofill' 154 }], 155 'type': 'Autofill' 156 }]; 157 158 /** 159 * A value to return in mock onReceivedUpdatedAboutInfo event. 160 * @const 161 */ 162 HARD_CODED_ABOUT_INFO = { 163 'actionable_error': [ 164 { 165 'is_valid': false, 166 'stat_name': 'Error Type', 167 'stat_value': 'Uninitialized' 168 }, 169 { 170 'is_valid': false, 171 'stat_name': 'Action', 172 'stat_value': 'Uninitialized' 173 }, 174 { 175 'is_valid': false, 176 'stat_name': 'URL', 177 'stat_value': 'Uninitialized' 178 }, 179 { 180 'is_valid': false, 181 'stat_name': 'Error Description', 182 'stat_value': 'Uninitialized' 183 } 184 ], 185 'actionable_error_detected': false, 186 'details': [ 187 { 188 'data': [ 189 { 190 'is_valid': true, 191 'stat_name': 'Summary', 192 'stat_value': 'Sync service initialized' 193 } 194 ], 195 'is_sensitive': false, 196 'title': 'Summary' 197 }, 198 ], 199 'type_status': [ 200 { 201 'name': 'Model Type', 202 'num_entries': 'Total Entries', 203 'num_live': 'Live Entries', 204 'status': 'header', 205 'value': 'Group Type' 206 }, 207 { 208 'name': 'Bookmarks', 209 'num_entries': 2793, 210 'num_live': 2793, 211 'status': 'ok', 212 'value': 'Active: GROUP_UI' 213 }, 214 ], 215 'unrecoverable_error_detected': false 216 }; 217 218 NETWORK_EVENT_DETAILS_1 = { 219 'details': 'Notified types: Bookmarks, Autofill', 220 'proto': {}, 221 'time': 1395874542192.407, 222 'type': 'Normal GetUpdate request', 223 }; 224 225 NETWORK_EVENT_DETAILS_2 = { 226 'details': 'Received error: SYNC_AUTH_ERROR', 227 'proto': {}, 228 'time': 1395874542192.837, 229 'type': 'GetUpdates Response', 230 }; 231 232 TEST_F('SyncInternalsWebUITest', 'Uninitialized', function() { 233 assertNotEquals(null, chrome.sync.aboutInfo); 234 expectTrue(this.hasInDetails(true, 'Username', '')); 235 expectTrue(this.hasInDetails(false, 'Summary', 'Uninitialized')); 236 }); 237 238 TEST_F('SyncInternalsWebUITest', 'LoadPastedAboutInfo', function() { 239 // Expose the text field. 240 $('import-status').click(); 241 242 // Fill it with fake data. 243 $('status-text').value = JSON.stringify(HARD_CODED_ABOUT_INFO); 244 245 // Trigger the import. 246 $('import-status').click(); 247 248 expectTrue(this.hasInDetails(true, 'Summary', 'Sync service initialized')); 249 }); 250 251 TEST_F('SyncInternalsWebUITest', 'NetworkEventsTest', function() { 252 networkEvent1 = new Event('onProtocolEvent'); 253 networkEvent1.details = NETWORK_EVENT_DETAILS_1; 254 networkEvent2 = new Event('onProtocolEvent'); 255 networkEvent2.details = NETWORK_EVENT_DETAILS_2; 256 257 chrome.sync.events.dispatchEvent(networkEvent1); 258 chrome.sync.events.dispatchEvent(networkEvent2); 259 260 expectEquals(2, $('traffic-event-container').children.length); 261 262 // Test that repeated events are not re-displayed. 263 chrome.sync.events.dispatchEvent(networkEvent1); 264 expectEquals(2, $('traffic-event-container').children.length); 265 }); 266 267 TEST_F('SyncInternalsWebUITest', 'SearchTabDoesntChangeOnItemSelect', 268 function() { 269 // Select the search tab. 270 $('sync-search-tab').selected = true; 271 expectTrue($('sync-search-tab').selected); 272 273 // Build the data model and attach to result list. 274 cr.ui.List.decorate($('sync-results-list')); 275 $('sync-results-list').dataModel = new cr.ui.ArrayDataModel([ 276 { 277 value: 'value 0', 278 toString: function() { return 'node 0'; }, 279 }, 280 { 281 value: 'value 1', 282 toString: function() { return 'node 1'; }, 283 } 284 ]); 285 286 // Select the first list item and verify the search tab remains selected. 287 $('sync-results-list').getListItemByIndex(0).selected = true; 288 expectTrue($('sync-search-tab').selected); 289 }); 290 291 TEST_F('SyncInternalsWebUITest', 'NodeBrowserTest', function() { 292 var getAllNodesSavedArgs = new SaveMockArguments(); 293 this.mockHandler.expects(once()). 294 getAllNodes(getAllNodesSavedArgs.match(ANYTHING)). 295 will(callFunctionWithSavedArgs(getAllNodesSavedArgs, 296 chrome.sync.getAllNodesCallback, 297 HARD_CODED_ALL_NODES)); 298 299 // Hit the refresh button. 300 $('node-browser-refresh-button').click(); 301 302 // Check that the refresh time was updated. 303 expectNotEquals($('node-browser-refresh-time').textContent, 'Never'); 304 305 // Verify some hard-coded assumptions. These depend on the vaue of the 306 // hard-coded nodes, specified elsewhere in this file. 307 308 // Start with the tree itself. 309 var tree = $('sync-node-tree'); 310 assertEquals(1, tree.items.length); 311 312 // Check the type root and expand it. 313 var typeRoot = tree.items[0]; 314 expectFalse(typeRoot.expanded); 315 typeRoot.expanded = true; 316 assertEquals(1, typeRoot.items.length); 317 318 // An actual sync node. The child of the type root. 319 var leaf = typeRoot.items[0]; 320 321 // Verify that selecting it affects the details view. 322 expectTrue($('node-details').hasAttribute('hidden')); 323 leaf.selected = true; 324 expectFalse($('node-details').hasAttribute('hidden')); 325 }); 326 327 TEST_F('SyncInternalsWebUITest', 'NodeBrowserRefreshOnTabSelect', function() { 328 var getAllNodesSavedArgs = new SaveMockArguments(); 329 this.mockHandler.expects(once()). 330 getAllNodes(getAllNodesSavedArgs.match(ANYTHING)). 331 will(callFunctionWithSavedArgs(getAllNodesSavedArgs, 332 chrome.sync.getAllNodesCallback, 333 HARD_CODED_ALL_NODES)); 334 335 // Should start with non-refreshed node browser. 336 expectEquals($('node-browser-refresh-time').textContent, 'Never'); 337 338 // Selecting the tab will refresh it. 339 $('sync-browser-tab').selected = true; 340 expectNotEquals($('node-browser-refresh-time').textContent, 'Never'); 341 342 // Re-selecting the tab shouldn't re-refresh. 343 $('node-browser-refresh-time').textContent = 'TestCanary'; 344 $('sync-browser-tab').selected = false; 345 $('sync-browser-tab').selected = true; 346 expectEquals($('node-browser-refresh-time').textContent, 'TestCanary'); 347 }); 348 349 // Tests that the events log page correctly receives and displays an event. 350 TEST_F('SyncInternalsWebUITest', 'EventLogTest', function() { 351 // Dispatch an event. 352 var connectionEvent = new Event('onConnectionStatusChange'); 353 connectionEvent.details = {'status': 'CONNECTION_OK'}; 354 chrome.sync.events.dispatchEvent(connectionEvent); 355 356 // Verify that it is displayed in the events log. 357 var syncEventsTable = $('sync-events'); 358 var firstRow = syncEventsTable.children[0]; 359 360 // Makes some assumptions about column ordering. We'll need re-think this if 361 // it turns out to be a maintenance burden. 362 assertEquals(4, firstRow.children.length); 363 var submoduleName = firstRow.children[1].textContent; 364 var eventName = firstRow.children[2].textContent; 365 var detailsText = firstRow.children[3].textContent; 366 367 expectGE(submoduleName.indexOf('manager'), 0, 368 'submoduleName=' + submoduleName); 369 expectGE(eventName.indexOf('onConnectionStatusChange'), 0, 370 'eventName=' + eventName); 371 expectGE(detailsText.indexOf('CONNECTION_OK'), 0, 372 'detailsText=' + detailsText); 373 }); 374 375 TEST_F('SyncInternalsWebUITest', 'DumpSyncEventsToText', function() { 376 // Dispatch an event. 377 var connectionEvent = new Event('onConnectionStatusChange'); 378 connectionEvent.details = {'status': 'CONNECTION_OK'}; 379 chrome.sync.events.dispatchEvent(connectionEvent); 380 381 // Click the dump-to-text button. 382 $('dump-to-text').click(); 383 384 // Verify our event is among the results. 385 var eventDumpText = $('data-dump').textContent; 386 387 expectGE(eventDumpText.indexOf('onConnectionStatusChange'), 0); 388 expectGE(eventDumpText.indexOf('CONNECTION_OK'), 0); 389 }); 390