• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
10function SyncInternalsWebUITest() {}
11
12SyncInternalsWebUITest.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 */
63var 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 */
162HARD_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
218NETWORK_EVENT_DETAILS_1 = {
219  'details': 'Notified types: Bookmarks, Autofill',
220  'proto': {},
221  'time': 1395874542192.407,
222  'type': 'Normal GetUpdate request',
223};
224
225NETWORK_EVENT_DETAILS_2 = {
226  'details': 'Received error: SYNC_AUTH_ERROR',
227  'proto': {},
228  'time': 1395874542192.837,
229  'type': 'GetUpdates Response',
230};
231
232TEST_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
238TEST_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
251TEST_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
267TEST_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
291TEST_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
327TEST_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.
350TEST_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
375TEST_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