sync_internals_browsertest.js revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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