Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / chrome / test / data / webui / history_browsertest.js
blobf024cbc4a8e6b7f643e7dd93fc1e8f3a58cb63fa
1 // Copyright (c) 2012 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.
5 GEN('#include "chrome/test/data/webui/history_ui_browsertest.h"');
7 /** @const */ var TOTAL_RESULT_COUNT = 160;
8 /** @const */ var WAIT_TIMEOUT = 200;
10 /**
11 * Test fixture for history WebUI testing.
12 * @constructor
13 * @extends {testing.Test}
15 function HistoryUIBrowserTest() {}
17 /**
18 * Create a fake history result with the given timestamp.
19 * @param {Number} timestamp Timestamp of the entry, in ms since the epoch.
20 * @param {String} url The URL to set on this entry.
21 * @return {Object} An object representing a history entry.
23 function createHistoryEntry(timestamp, url) {
24 var d = new Date(timestamp);
25 // Extract domain from url.
26 var domainMatch = url.replace(/^.+?:\/\//, '').match(/[^/]+/);
27 var domain = domainMatch ? domainMatch[0] : '';
28 return {
29 dateTimeOfDay: d.getHours() + ':' + d.getMinutes(),
30 dateRelativeDay: d.toDateString(),
31 allTimestamps: [timestamp],
32 starred: false,
33 time: timestamp,
34 title: d.toString(), // Use the stringified date as the title.
35 url: url,
36 domain: domain
40 /**
41 * Wait for the history backend to call the global function named
42 * |callbackName|, and then execute |afterFunction|. This allows tests to
43 * wait on asynchronous backend operations before proceeding.
45 function waitForCallback(callbackName, afterFunction) {
46 var originalCallback = window[callbackName];
48 // Install a wrapper that temporarily replaces the original function.
49 window[callbackName] = function() {
50 window[callbackName] = originalCallback;
51 originalCallback.apply(this, arguments);
52 afterFunction();
56 /**
57 * Asynchronously execute the global function named |functionName|. This
58 * should be used for all calls from backend stubs to the frontend.
60 function callFrontendAsync(functionName) {
61 var args = Array.prototype.slice.call(arguments, 1);
62 setTimeout(function() {
63 window[functionName].apply(window, args);
64 }, 1);
67 /**
68 * Checks that all the checkboxes in the [|start|, |end|] interval are checked
69 * and that their IDs are properly set. Does that against the checkboxes in
70 * |checked|, starting from the |startInChecked| position.
71 * @param {Array} checked An array of all the relevant checked checkboxes
72 * on this page.
73 * @param {Number} start The starting checkbox id.
74 * @param {Number} end The ending checkbox id.
76 function checkInterval(checked, start, end) {
77 for (var i = start; i <= end; i++)
78 expectEquals('checkbox-' + i, checked[i - start].id);
81 /**
82 * Returns a period of 7 days, |offset| weeks back from |today|. The behavior
83 * of this function should be identical to
84 * BrowsingHistoryHandler::SetQueryTimeInWeeks.
85 * @param {Number} offset Number of weeks to go back.
86 * @param {Date} today Which date to consider as "today" (since we're not using
87 * the actual current date in this case).
88 * @return {Object} An object containing the begin date and the end date of the
89 * computed period.
91 function setQueryTimeInWeeks(offset, today) {
92 // Going back one day at a time starting from midnight will make sure that
93 // the other values get updated properly.
94 var endTime = new Date(today);
95 endTime.setHours(24, 0, 0, 0);
96 for (var i = 0; i < 7 * offset; i++)
97 endTime.setDate(endTime.getDate() - 1);
98 var beginTime = new Date(endTime);
99 for (var i = 0; i < 7; i++)
100 beginTime.setDate(beginTime.getDate() - 1);
101 return {'endTime': endTime, 'beginTime': beginTime};
105 * Returns the period of a month, |offset| months back from |today|. The
106 * behavior of this function should be identical to
107 * BrowsingHistoryHandler::SetQueryTimeInMonths.
108 * @param {Number} offset Number of months to go back.
109 * @param {Date} today Which date to consider as "today" (since we're not using
110 * the actual current date in this case).
111 * @return {Object} An object containing the begin date and the end date of the
112 * computed period.
114 function setQueryTimeInMonths(offset, today) {
115 var endTime = new Date(today);
116 var beginTime = new Date(today);
117 // Last day of this month.
118 endTime.setMonth(endTime.getMonth() + 1, 0);
119 // First day of the current month.
120 beginTime.setMonth(beginTime.getMonth(), 1);
121 for (var i = 0; i < offset; i++) {
122 beginTime.setMonth(beginTime.getMonth() - 1);
123 endTime.setMonth(endTime.getMonth() - 1);
125 return {'endTime': endTime, 'beginTime': beginTime};
129 * Base fixture for History WebUI testing.
130 * @extends {testing.Test}
131 * @constructor
133 function BaseHistoryWebUITest() {}
135 BaseHistoryWebUITest.prototype = {
136 __proto__: testing.Test.prototype,
139 * Browse to the history page & call our preLoad().
141 browsePreload: 'chrome://history-frame',
143 /** @override */
144 typedefCppFixture: 'HistoryUIBrowserTest',
146 /** @override */
147 runAccessibilityChecks: true,
149 /** @override */
150 accessibilityIssuesAreErrors: true,
152 /** @override */
153 isAsync: true,
157 * Fixture for History WebUI testing which returns some fake history results
158 * to the frontend. Other fixtures that want to stub out calls to the backend
159 * can extend this one.
160 * @extends {BaseHistoryWebUITest}
161 * @constructor
163 function HistoryWebUIFakeBackendTest() {
166 HistoryWebUIFakeBackendTest.prototype = {
167 __proto__: BaseHistoryWebUITest.prototype,
170 * Register handlers to stub out calls to the history backend.
171 * @override
173 preLoad: function() {
174 this.registerMockHandler_(
175 'queryHistory', this.queryHistoryStub_.bind(this));
179 * Register a mock handler for a message to the history backend.
180 * @param handlerName The name of the message to mock.
181 * @param handler The mock message handler function.
183 registerMockHandler_: function(handlerName, handler) {
184 // Mock4JS doesn't pass in the actual arguments to the stub, but it _will_
185 // pass the original args to the matcher object. SaveMockArguments acts as
186 // a proxy for another matcher, but keeps track of all the arguments it was
187 // asked to match.
188 var savedArgs = new SaveMockArguments();
190 this.makeAndRegisterMockHandler([handlerName]);
191 this.mockHandler.stubs()[handlerName](savedArgs.match(ANYTHING)).will(
192 callFunctionWithSavedArgs(savedArgs, handler));
196 * Default stub for the queryHistory message to the history backend.
197 * Simulates an empty history database. Override this to customize this
198 * behavior for particular tests.
199 * @param {Array} arguments The original arguments to queryHistory.
201 queryHistoryStub_: function(args) {
202 callFrontendAsync(
203 'historyResult', { term: args[0], finished: true }, []);
207 function queryHistoryImpl(args, beginTime, history) {
208 var searchText = args[0];
209 var offset = args[1];
210 var range = args[2];
211 var endTime = args[3] || Number.MAX_VALUE;
212 var maxCount = args[4];
214 var results = [];
215 if (searchText) {
216 for (var k = 0; k < history.length; k++) {
217 // Search only by title in this stub.
218 if (history[k].title.indexOf(searchText) != -1)
219 results.push(history[k]);
221 } else {
222 results = history;
225 // Advance past all entries newer than the specified end time.
226 var i = 0;
227 // Finished is set from the history database so this behavior may not be
228 // completely identical.
229 var finished = true;
230 while (i < results.length && results[i].time >= endTime)
231 ++i;
233 if (beginTime) {
234 var j = i;
235 while (j < results.length && results[j].time >= beginTime)
236 ++j;
238 finished = (j == results.length);
239 results = results.slice(i, j);
240 } else {
241 results = results.slice(i);
244 if (maxCount) {
245 finished = (maxCount >= results.length);
246 results = results.slice(0, maxCount);
249 var queryStartTime = '';
250 var queryEndTime = '';
251 if (results.length) {
252 queryStartTime = results[results.length - 1].dateRelativeDay;
253 queryEndTime = results[0].dateRelativeDay;
254 } else if (beginTime) {
255 queryStartTime = Date(beginTime);
256 queryEndTime = Date(endTime);
259 callFrontendAsync(
260 'historyResult',
262 term: searchText,
263 finished: finished,
264 queryStartTime: queryStartTime,
265 queryEndTime: queryEndTime
267 results);
271 * Fixture for History WebUI testing which returns some fake history results
272 * to the frontend.
273 * @extends {HistoryWebUIFakeBackendTest}
274 * @constructor
276 function HistoryWebUITest() {}
278 HistoryWebUITest.prototype = {
279 __proto__: HistoryWebUIFakeBackendTest.prototype,
281 preLoad: function() {
282 HistoryWebUIFakeBackendTest.prototype.preLoad.call(this);
284 this.registerMockHandler_(
285 'removeVisits', this.removeVisitsStub_.bind(this));
287 // Prepare a list of fake history results. The entries will begin at
288 // 1:00 AM on Sept 2, 2008, and will be spaced two minutes apart.
289 var timestamp = new Date(2008, 9, 2, 1, 0).getTime();
290 this.fakeHistory_ = [];
292 for (var i = 0; i < TOTAL_RESULT_COUNT; i++) {
293 this.fakeHistory_.push(
294 createHistoryEntry(timestamp, 'http://google.com/' + timestamp));
295 timestamp -= 2 * 60 * 1000; // Next visit is two minutes earlier.
300 * Stub for the 'queryHistory' message to the history backend.
301 * Simulates a history database using the fake history data that is
302 * initialized in preLoad().
303 * @param {Array} arguments The original arguments to queryHistory.
305 queryHistoryStub_: function(args) {
306 var searchText = args[0];
307 var offset = args[1];
308 var range = args[2];
309 var endTime = args[3] || Number.MAX_VALUE;
310 var maxCount = args[4];
311 if (range == HistoryModel.Range.ALL_TIME) {
312 queryHistoryImpl(args, null, this.fakeHistory_);
313 return;
315 if (range == HistoryModel.Range.WEEK)
316 var interval = setQueryTimeInWeeks(offset, this.today);
317 else
318 var interval = setQueryTimeInMonths(offset, this.today);
320 args[3] = interval.endTime.getTime();
321 queryHistoryImpl(args, interval.beginTime.getTime(), this.fakeHistory_);
325 * Stub for the 'removeVisits' message to the history backend.
326 * This will modify the fake history data in the test instance, so that
327 * further 'queryHistory' messages will not contain the deleted entries.
328 * @param {Array} arguments The original arguments to removeVisits.
330 removeVisitsStub_: function(args) {
331 for (var i = 0; i < args.length; ++i) {
332 var url = args[i].url;
333 var timestamps = args[i].timestamps;
334 assertEquals(timestamps.length, 1);
335 this.removeVisitsToUrl_(url, new Date(timestamps[0]));
337 callFrontendAsync('deleteComplete');
341 * Removes any visits to |url| on the same day as |date| from the fake
342 * history data.
343 * @param {string} url
344 * @param {Date} date
346 removeVisitsToUrl_: function(url, date) {
347 var day = date.toDateString();
348 var newHistory = [];
349 for (var i = 0, visit; visit = this.fakeHistory_[i]; ++i) {
350 if (url != visit.url || visit.dateRelativeDay != day)
351 newHistory.push(visit);
353 this.fakeHistory_ = newHistory;
358 * Examines the time column of every entry on the page, and ensure that they
359 * are all the same width.
361 function ensureTimeWidthsEqual() {
362 var times = document.querySelectorAll('.entry .time');
363 var timeWidth = times[0].clientWidth;
364 for (var i = 1; i < times.length; ++i) {
365 assertEquals(timeWidth, times[i].clientWidth);
369 // Times out on Mac: http://crbug.com/336845
370 TEST_F('HistoryWebUIFakeBackendTest', 'DISABLED_emptyHistory', function() {
371 expectTrue($('newest-button').hidden);
372 expectTrue($('newer-button').hidden);
373 expectTrue($('older-button').hidden);
374 testDone();
377 // Times out on Win: http://crbug.com/336845
378 TEST_F('HistoryWebUITest', 'DISABLED_basicTest', function() {
379 var resultCount = document.querySelectorAll('.entry').length;
381 // Check that there are two days of entries.
382 var dayHeaders = document.querySelectorAll('.day');
383 assertEquals(2, dayHeaders.length);
384 expectNotEquals(dayHeaders[0].textContent, dayHeaders[1].textContent);
386 // Check that the entries in each day are time-ordered, and that no
387 // duplicate URLs appear on a given day.
388 var urlsByDay = {};
389 var lastDate = new Date();
390 for (var day = 0; day < dayHeaders.length; ++day) {
391 var dayTitle = dayHeaders[day].textContent;
392 var dayResults = document.querySelectorAll('.day-results')[day];
393 var entries = dayResults.querySelectorAll('.entry');
394 expectGT(entries.length, 0);
396 for (var i = 0, entry; entry = entries[i]; ++i) {
397 var time = entry.querySelector('.time').textContent;
398 expectGT(time.length, 0);
400 var date = new Date(dayTitle + ' ' + time);
401 expectGT(lastDate, date);
402 lastDate = date;
404 // Ensure it's not a duplicate URL for this day.
405 var dayAndUrl = day + entry.querySelector('a').href;
406 expectFalse(urlsByDay.hasOwnProperty(dayAndUrl));
407 urlsByDay[dayAndUrl] = dayAndUrl;
409 // Reconstruct the entry date from the title, and ensure that it's
410 // consistent with the date header and with the time.
411 var entryDate = new Date(entry.querySelector('.title').textContent);
412 expectEquals(entryDate.getYear(), date.getYear());
413 expectEquals(entryDate.getMonth(), date.getMonth());
414 expectEquals(entryDate.getDay(), date.getDay());
415 expectEquals(entryDate.getHours(), date.getHours());
416 expectEquals(entryDate.getMinutes(), date.getMinutes());
420 // Check that there are 3 page navigation links and that only the "Older"
421 // link is visible.
422 expectEquals(3, document.querySelectorAll('[is="action-link"]').length);
423 expectTrue($('newest-button').hidden);
424 expectTrue($('newer-button').hidden);
425 expectFalse($('older-button').hidden);
427 ensureTimeWidthsEqual();
429 // Go to the next page.
430 $('older-button').click();
431 waitForCallback('historyResult', function() {
432 resultCount += document.querySelectorAll('.entry').length;
434 // Check that the two pages include all of the entries.
435 expectEquals(TOTAL_RESULT_COUNT, resultCount);
437 // Check that the day header was properly continued -- the header for the
438 // last day on the first page should be a substring of the header on the
439 // second page. E.g. "Wed, Oct 8, 2008" and "Web, Oct 8, 2008 - cont'd".
440 var newDayHeaders = document.querySelectorAll('.day');
441 expectEquals(1, newDayHeaders.length);
442 expectEquals(0,
443 newDayHeaders[0].textContent.indexOf(dayHeaders[1].textContent));
445 // Check that the "Newest" and "Newer" links are now visible, but the
446 // "Older" link is hidden.
447 expectEquals(3, document.querySelectorAll('[is="action-link"]').length);
448 expectFalse($('newest-button').hidden);
449 expectFalse($('newer-button').hidden);
450 expectTrue($('older-button').hidden);
452 ensureTimeWidthsEqual();
454 // Go back to the first page, and check that the same day headers are there.
455 $('newest-button').click();
456 var newDayHeaders = document.querySelectorAll('.day');
457 expectEquals(2, newDayHeaders.length);
459 expectNotEquals(newDayHeaders[0].textContent,
460 newDayHeaders[1].textContent);
461 expectEquals(dayHeaders[0].textContent, newDayHeaders[0].textContent);
462 expectEquals(dayHeaders[1].textContent, newDayHeaders[1].textContent);
464 testDone();
469 * Test bulk deletion of history entries.
470 * Disabled because it is currently very flaky on the Windows XP bot.
472 TEST_F('HistoryWebUITest', 'DISABLED_bulkDeletion', function() {
473 var checkboxes = document.querySelectorAll(
474 '#results-display input[type=checkbox]');
476 // Immediately confirm the history deletion.
477 confirmDeletion = function(okCallback, cancelCallback) {
478 okCallback();
481 // The "remove" button should be initially disabled.
482 var removeButton = $('remove-selected');
483 expectTrue(removeButton.disabled);
485 checkboxes[0].click();
486 expectFalse(removeButton.disabled);
488 var firstEntry = document.querySelector('.title a').textContent;
489 removeButton.click();
491 // After deletion, expect the results to be reloaded.
492 waitForCallback('historyResult', function() {
493 expectNotEquals(document.querySelector('.title a').textContent, firstEntry);
494 expectTrue(removeButton.disabled);
496 // Delete the first 3 entries.
497 checkboxes = document.querySelectorAll(
498 '#results-display input[type=checkbox]');
499 checkboxes[0].click();
500 checkboxes[1].click();
501 checkboxes[2].click();
502 expectFalse(removeButton.disabled);
504 var nextEntry = document.querySelectorAll('.title a')[3];
505 removeButton.click();
506 waitForCallback('historyResult', function() {
507 // The next entry after the deleted ones should now be the first.
508 expectEquals(document.querySelector('.title a').textContent,
509 nextEntry.textContent);
510 testDone();
516 * Test selecting multiple entries using shift click.
517 * Disabled due to time out on all platforms: crbug/375910
519 TEST_F('HistoryWebUITest', 'DISABLED_multipleSelect', function() {
520 var checkboxes = document.querySelectorAll(
521 '#results-display input[type=checkbox]');
523 var getAllChecked = function() {
524 return Array.prototype.slice.call(document.querySelectorAll(
525 '#results-display input[type=checkbox]:checked'));
528 // Make sure that nothing is checked.
529 expectEquals(0, getAllChecked().length);
531 var shiftClick = function(el) {
532 el.dispatchEvent(new MouseEvent('click', { shiftKey: true }));
535 // Check the start.
536 shiftClick($('checkbox-4'));
537 // And the end.
538 shiftClick($('checkbox-9'));
540 // See if they are checked.
541 var checked = getAllChecked();
542 expectEquals(6, checked.length);
543 checkInterval(checked, 4, 9);
545 // Extend the selection.
546 shiftClick($('checkbox-14'));
548 checked = getAllChecked();
549 expectEquals(11, checked.length);
550 checkInterval(checked, 4, 14);
552 // Now do a normal click on a higher ID box and a shift click on a lower ID
553 // one (test the other way around).
554 $('checkbox-24').click();
555 shiftClick($('checkbox-19'));
557 checked = getAllChecked();
558 expectEquals(17, checked.length);
559 // First set of checkboxes (11).
560 checkInterval(checked, 4, 14);
561 // Second set (6).
562 checkInterval(checked.slice(11), 19, 24);
564 // Test deselection.
565 $('checkbox-26').click();
566 shiftClick($('checkbox-20'));
568 checked = getAllChecked();
569 // checkbox-20 to checkbox-24 should be deselected now.
570 expectEquals(12, checked.length);
571 // First set of checkboxes (11).
572 checkInterval(checked, 4, 14);
573 // Only checkbox-19 should still be selected.
574 expectEquals('checkbox-19', checked[11].id);
576 testDone();
579 TEST_F('HistoryWebUITest', 'DISABLED_searchHistory', function() {
580 var getResultCount = function() {
581 return document.querySelectorAll('.entry').length;
583 // See that all the elements are there.
584 expectEquals(RESULTS_PER_PAGE, getResultCount());
586 // See that the search works.
587 $('search-field').value = 'Thu Oct 02 2008';
588 $('search-button').click();
590 waitForCallback('historyResult', function() {
591 expectEquals(31, getResultCount());
593 // Clear the search.
594 $('search-field').value = '';
595 $('search-button').click();
596 waitForCallback('historyResult', function() {
597 expectEquals(RESULTS_PER_PAGE, getResultCount());
598 testDone();
603 function setPageState(searchText, page, groupByDomain, range, offset) {
604 window.location = '#' + PageState.getHashString(
605 searchText, page, groupByDomain, range, offset);
608 function RangeHistoryWebUITest() {}
610 RangeHistoryWebUITest.prototype = {
611 __proto__: HistoryWebUITest.prototype,
613 /** @override */
614 preLoad: function() {
615 HistoryWebUITest.prototype.preLoad.call(this);
616 // Repeat the domain visits every 4 days. The nested lists contain the
617 // domain suffixes for the visits in a day.
618 var domainSuffixByDay = [
619 [1, 2, 3, 4],
620 [1, 2, 2, 3],
621 [1, 2, 1, 2],
622 [1, 1, 1, 1]
625 var buildDomainUrl = function(timestamp) {
626 var d = new Date(timestamp);
627 // Repeat the same setup of domains every 4 days.
628 var day = d.getDate() % 4;
629 // Assign an entry for every 6 hours so that we get 4 entries per day
630 // maximum.
631 var visitInDay = Math.floor(d.getHours() / 6);
632 return 'http://google' + domainSuffixByDay[day][visitInDay] + '.com/' +
633 timestamp;
636 // Prepare a list of fake history results. Start the results on
637 // 11:00 PM on May 2, 2012 and add 4 results every day (one result every 6
638 // hours).
639 var timestamp = new Date(2012, 4, 2, 23, 0).getTime();
640 this.today = new Date(2012, 4, 2);
641 this.fakeHistory_ = [];
643 // Put in 2 days for May and 30 days for April so the results span over
644 // the month limit.
645 for (var i = 0; i < 4 * 32; i++) {
646 this.fakeHistory_.push(
647 createHistoryEntry(timestamp, buildDomainUrl(timestamp)));
648 timestamp -= 6 * 60 * 60 * 1000;
651 // Leave March empty.
652 timestamp -= 31 * 24 * 3600 * 1000;
654 // Put results in February.
655 for (var i = 0; i < 29 * 4; i++) {
656 this.fakeHistory_.push(
657 createHistoryEntry(timestamp, buildDomainUrl(timestamp)));
658 timestamp -= 6 * 60 * 60 * 1000;
662 setUp: function() {
663 // Show the filter controls as if the command line switch was active.
664 $('top-container').hidden = true;
665 $('history-page').classList.add('big-topbar-page');
666 $('filter-controls').hidden = false;
667 expectFalse($('filter-controls').hidden);
672 * Disabled due intermitent failures on multiple OSes http://crbug.com/377338
674 TEST_F('RangeHistoryWebUITest', 'DISABLED_allView', function() {
675 // Check that we start off in the all time view.
676 expectTrue($('timeframe-controls').querySelector('input').checked);
677 // See if the correct number of days is shown.
678 var dayHeaders = document.querySelectorAll('.day');
679 assertEquals(Math.ceil(RESULTS_PER_PAGE / 4), dayHeaders.length);
680 testDone();
684 * Checks whether the domains in a day are ordered decreasingly.
685 * @param {Element} element Ordered list containing the grouped domains for a
686 * day.
688 function checkGroupedVisits(element) {
689 // The history page contains the number of visits next to a domain in
690 // parentheses (e.g. 'google.com (5)'). This function extracts that number
691 // and returns it.
692 var getNumberVisits = function(element) {
693 return parseInt(element.textContent.replace(/\D/g, ''), 10);
696 // Read the number of visits from each domain and make sure that it is lower
697 // than or equal to the number of visits from the previous domain.
698 var domainEntries = element.querySelectorAll('.number-visits');
699 var currentNumberOfVisits = getNumberVisits(domainEntries[0]);
700 for (var j = 1; j < domainEntries.length; j++) {
701 var numberOfVisits = getNumberVisits(domainEntries[j]);
702 assertTrue(currentNumberOfVisits >= numberOfVisits);
703 currentNumberOfVisits = numberOfVisits;
707 // Times out on Mac and Win: http://crbug.com/336845
708 TEST_F('RangeHistoryWebUITest', 'DISABLED_weekViewGrouped', function() {
709 // Change to weekly view.
710 setPageState('', 0, HistoryModel.Range.WEEK, 0);
711 waitForCallback('historyResult', function() {
712 // See if the correct number of days is still shown.
713 var dayResults = document.querySelectorAll('.day-results');
714 assertEquals(7, dayResults.length);
716 // Check whether the results are ordered by visits.
717 for (var i = 0; i < dayResults.length; i++)
718 checkGroupedVisits(dayResults[i]);
720 ensureTimeWidthsEqual();
722 testDone();
726 // Times out on Mac and Win: http://crbug.com/336845
727 TEST_F('RangeHistoryWebUITest', 'DISABLED_monthViewGrouped', function() {
728 // Change to monthly view.
729 setPageState('', 0, HistoryModel.Range.MONTH, 0);
730 waitForCallback('historyResult', function() {
731 // See if the correct number of days is shown.
732 var monthResults = document.querySelectorAll('.month-results');
733 assertEquals(1, monthResults.length);
735 checkGroupedVisits(monthResults[0]);
736 ensureTimeWidthsEqual();
738 testDone();
742 // Times out on Mac: http://crbug.com/336845
743 TEST_F('RangeHistoryWebUITest', 'DISABLED_monthViewEmptyMonth', function() {
744 // Change to monthly view.
745 setPageState('', 0, HistoryModel.Range.MONTH, 2);
747 waitForCallback('historyResult', function() {
748 // See if the correct number of days is shown.
749 var resultsDisplay = $('results-display');
750 assertEquals(0, resultsDisplay.querySelectorAll('.months-results').length);
751 var noResults = loadTimeData.getString('noResults');
752 assertNotEquals(-1, $('results-header').textContent.indexOf(noResults));
754 testDone();
759 * Fixture for History WebUI tests using the real history backend.
760 * @extends {BaseHistoryWebUITest}
761 * @constructor
763 function HistoryWebUIRealBackendTest() {}
765 HistoryWebUIRealBackendTest.prototype = {
766 __proto__: BaseHistoryWebUITest.prototype,
768 /** @override */
769 testGenPreamble: function() {
770 // Add some visits to the history database.
771 GEN(' AddPageToHistory(0, "http://google.com", "Google");');
772 GEN(' AddPageToHistory(1, "http://example.com", "Example");');
773 GEN(' AddPageToHistory(2, "http://google.com", "Google");');
775 // Add a visit on the next day.
776 GEN(' AddPageToHistory(36, "http://google.com", "Google");');
781 * Simple test that verifies that the correct entries are retrieved from the
782 * history database and displayed in the UI.
784 // Times out on Mac and Win: http://crbug.com/336845
785 TEST_F('HistoryWebUIRealBackendTest', 'DISABLED_basic', function() {
786 // Check that there are two days of entries, and three entries in total.
787 assertEquals(2, document.querySelectorAll('.day').length);
788 assertEquals(3, document.querySelectorAll('.entry').length);
790 testDone();
793 // Times out on Mac: http://crbug.com/336845
794 TEST_F('HistoryWebUIRealBackendTest',
795 'DISABLED_atLeastOneFocusable', function() {
796 var results = document.querySelectorAll('#results-display [tabindex="0"]');
797 expectGE(results.length, 1);
798 testDone();
801 // Times out on Mac: http://crbug.com/336845
802 TEST_F('HistoryWebUIRealBackendTest',
803 'DISABLED_deleteRemovesEntry', function() {
804 assertTrue(historyModel.deletingHistoryAllowed);
806 var visit = document.querySelector('.entry').visit;
807 visit.titleLink.focus();
808 assertEquals(visit.titleLink, document.activeElement);
810 var deleteKey = document.createEvent('KeyboardEvent');
811 deleteKey.initKeyboardEvent('keydown', true, true, window, 'U+007F');
812 assertEquals('U+007F', deleteKey.keyIdentifier);
814 assertFalse(historyModel.isDeletingVisits());
815 expectFalse(visit.titleLink.dispatchEvent(deleteKey));
816 expectTrue(historyModel.isDeletingVisits());
818 expectNotEquals(visit.dropDown, document.activeElement);
819 testDone();
823 * Test individual deletion of history entries.
825 TEST_F('HistoryWebUIRealBackendTest', 'singleDeletion', function() {
826 // Deletes the history entry represented by |entryElement|, and calls callback
827 // when the deletion is complete.
828 var removeEntry = function(entryElement, callback) {
829 var dropDownButton = entryElement.querySelector('.drop-down');
830 var removeMenuItem = $('remove-visit');
832 assertFalse(dropDownButton.disabled);
833 assertFalse(removeMenuItem.disabled);
835 waitForCallback('onEntryRemoved', callback);
837 cr.dispatchSimpleEvent(dropDownButton, 'mousedown');
839 var e = new Event('command', {bubbles: true});
840 e.command = removeMenuItem.command;
841 removeMenuItem.dispatchEvent(e);
844 var secondTitle = document.querySelectorAll('.entry a')[1].textContent;
845 var thirdTitle = document.querySelectorAll('.entry a')[2].textContent;
847 // historyDeleted() should not be called when deleting individual entries
848 // using the drop down.
849 waitForCallback('historyDeleted', function() {
850 testDone([false, 'historyDeleted() called when deleting single entry']);
853 expectEquals(2, document.querySelectorAll('.day').length);
855 // Delete the first entry. The previous second entry should now be the first.
856 removeEntry(document.querySelector('.entry'), function() {
857 expectEquals(secondTitle, document.querySelector('.entry a').textContent);
859 // After removing the first entry, its day header should also be gone.
860 expectEquals(1, document.querySelectorAll('.day').length);
862 // Delete another entry. The original third entry should now be the first.
863 removeEntry(document.querySelector('.entry'), function() {
864 expectEquals(thirdTitle, document.querySelector('.entry a').textContent);
865 testDone();
870 TEST_F('HistoryWebUIRealBackendTest', 'leftRightChangeFocus', function() {
871 var visit = document.querySelector('.entry').visit;
872 visit.titleLink.focus();
873 assertEquals(visit.titleLink, document.activeElement);
875 var right = document.createEvent('KeyboardEvent');
876 right.initKeyboardEvent('keydown', true, true, window, 'Right');
877 assertEquals('Right', right.keyIdentifier);
878 expectFalse(visit.titleLink.dispatchEvent(right));
880 assertEquals(visit.dropDown, document.activeElement);
882 var left = document.createEvent('KeyboardEvent');
883 left.initKeyboardEvent('keydown', true, true, window, 'Left');
884 assertEquals('Left', left.keyIdentifier);
885 expectFalse(visit.dropDown.dispatchEvent(left));
887 expectEquals(visit.titleLink, document.activeElement);
888 testDone();
891 TEST_F('HistoryWebUIRealBackendTest', 'showConfirmDialogAndCancel', function() {
892 waitForCallback('deleteComplete', function() {
893 testDone([false, "history deleted when it shouldn't have been"]);
896 document.querySelector('input[type=checkbox]').click();
897 $('remove-selected').click();
899 assertTrue($('alertOverlay').classList.contains('showing'));
900 assertFalse($('history-page').contains(document.activeElement));
902 var esc = document.createEvent('KeyboardEvent');
903 esc.initKeyboardEvent('keydown', true, true, window, 'U+001B');
905 document.documentElement.dispatchEvent(esc);
906 assertFalse($('alertOverlay').classList.contains('showing'));
908 testDone();
911 TEST_F('HistoryWebUIRealBackendTest', 'showConfirmDialogAndRemove', function() {
912 document.querySelector('input[type=checkbox]').click();
913 $('remove-selected').click();
915 assertTrue($('alertOverlay').classList.contains('showing'));
916 assertFalse($('history-page').contains(document.activeElement));
918 waitForCallback('deleteComplete', testDone);
920 var enter = document.createEvent('KeyboardEvent');
921 enter.initKeyboardEvent('keydown', true, true, window, 'Enter');
922 document.documentElement.dispatchEvent(enter);
923 assertFalse($('alertOverlay').classList.contains('showing'));
926 // Times out on Mac: http://crbug.com/336845
927 TEST_F('HistoryWebUIRealBackendTest',
928 'DISABLED_menuButtonActivatesOneRow', function() {
929 var entries = document.querySelectorAll('.entry');
930 assertEquals(3, entries.length);
931 assertTrue(entries[0].classList.contains('active'));
932 assertTrue($('action-menu').hidden);
934 // Show the menu via mousedown on the menu button.
935 var menuButton = entries[2].querySelector('.menu-button');
936 menuButton.dispatchEvent(new MouseEvent('mousedown'));
937 expectFalse($('action-menu').hidden);
939 // Check that the 'active' item has changed.
940 expectTrue(entries[2].classList.contains('active'));
941 expectFalse(entries[0].classList.contains('active'));
943 testDone();
946 TEST_F('HistoryWebUIRealBackendTest', 'shiftClickActivatesOneRow', function() {
947 var entries = document.querySelectorAll('.entry');
948 assertEquals(3, entries.length);
949 assertTrue(entries[0].classList.contains('active'));
951 entries[0].visit.checkBox.focus();
952 assertEquals(entries[0].visit.checkBox, document.activeElement);
954 entries[0].visit.checkBox.click();
955 assertTrue(entries[0].visit.checkBox.checked);
957 var entryBox = entries[2].querySelector('.entry-box');
958 entryBox.dispatchEvent(new MouseEvent('click', {shiftKey: true}));
959 assertTrue(entries[1].visit.checkBox.checked);
961 // Focus shouldn't have changed, but the checkbox should toggle.
962 expectEquals(entries[0].visit.checkBox, document.activeElement);
964 expectTrue(entries[0].classList.contains('active'));
965 expectFalse(entries[2].classList.contains('active'));
967 var shiftDown = new MouseEvent('mousedown', {shiftKey: true, bubbles: true});
968 entries[2].visit.checkBox.dispatchEvent(shiftDown);
969 expectEquals(entries[2].visit.checkBox, document.activeElement);
971 // 'focusin' events aren't dispatched while tests are run in batch (e.g.
972 // --test-launcher-jobs=2). Simulate this. TODO(dbeam): fix instead.
973 cr.dispatchSimpleEvent(document.activeElement, 'focusin', true, true);
975 expectFalse(entries[0].classList.contains('active'));
976 expectTrue(entries[2].classList.contains('active'));
978 testDone();
982 * Fixture for History WebUI testing when deletions are prohibited.
983 * @extends {HistoryWebUIRealBackendTest}
984 * @constructor
986 function HistoryWebUIDeleteProhibitedTest() {}
988 HistoryWebUIDeleteProhibitedTest.prototype = {
989 __proto__: HistoryWebUIRealBackendTest.prototype,
991 /** @override */
992 testGenPreamble: function() {
993 HistoryWebUIRealBackendTest.prototype.testGenPreamble.call(this);
994 GEN(' SetDeleteAllowed(false);');
998 // Test UI when removing entries is prohibited.
999 // Times out on Mac: http://crbug.com/336845
1000 TEST_F('HistoryWebUIDeleteProhibitedTest',
1001 'DISABLED_deleteProhibited', function() {
1002 // No checkboxes should be created.
1003 var checkboxes = document.querySelectorAll(
1004 '#results-display input[type=checkbox]');
1005 expectEquals(0, checkboxes.length);
1007 // The "remove" button should be disabled.
1008 var removeButton = $('remove-selected');
1009 expectTrue(removeButton.disabled);
1011 // The "Remove from history" drop-down item should be disabled.
1012 var removeVisit = $('remove-visit');
1013 expectTrue(removeVisit.disabled);
1015 testDone();
1018 TEST_F('HistoryWebUIDeleteProhibitedTest', 'atLeastOneFocusable', function() {
1019 var results = document.querySelectorAll('#results-display [tabindex="0"]');
1020 expectGE(results.length, 1);
1021 testDone();
1024 TEST_F('HistoryWebUIDeleteProhibitedTest', 'leftRightChangeFocus', function() {
1025 var visit = document.querySelector('.entry').visit;
1026 visit.titleLink.focus();
1027 assertEquals(visit.titleLink, document.activeElement);
1029 var right = document.createEvent('KeyboardEvent');
1030 right.initKeyboardEvent('keydown', true, true, window, 'Right');
1031 assertEquals('Right', right.keyIdentifier);
1032 expectFalse(visit.titleLink.dispatchEvent(right));
1034 assertEquals(visit.dropDown, document.activeElement);
1036 var left = document.createEvent('KeyboardEvent');
1037 left.initKeyboardEvent('keydown', true, true, window, 'Left');
1038 assertEquals('Left', left.keyIdentifier);
1039 expectFalse(visit.dropDown.dispatchEvent(left));
1041 expectEquals(visit.titleLink, document.activeElement);
1042 testDone();
1045 TEST_F('HistoryWebUIDeleteProhibitedTest', 'deleteIgnored', function() {
1046 assertFalse(historyModel.deletingHistoryAllowed);
1048 var visit = document.querySelector('.entry').visit;
1049 visit.titleLink.focus();
1050 assertEquals(visit.titleLink, document.activeElement);
1052 var deleteKey = document.createEvent('KeyboardEvent');
1053 deleteKey.initKeyboardEvent('keydown', true, true, window, 'U+007F');
1054 assertEquals('U+007F', deleteKey.keyIdentifier);
1056 assertFalse(historyModel.isDeletingVisits());
1057 expectTrue(visit.titleLink.dispatchEvent(deleteKey));
1058 expectFalse(historyModel.isDeletingVisits());
1060 expectEquals(visit.titleLink, document.activeElement);
1061 testDone();
1065 * Fixture for History WebUI testing IDN.
1066 * @extends {BaseHistoryWebUITest}
1067 * @constructor
1069 function HistoryWebUIIDNTest() {}
1071 HistoryWebUIIDNTest.prototype = {
1072 __proto__: BaseHistoryWebUITest.prototype,
1074 /** @override */
1075 testGenPreamble: function() {
1076 // Add some visits to the history database.
1077 GEN(' AddPageToHistory(0, "http://xn--d1abbgf6aiiy.xn--p1ai/",' +
1078 ' "Some");');
1080 // Clear AcceptLanguages to get domain in unicode.
1081 GEN(' ClearAcceptLanguages();');
1086 * Simple test that verifies that the correct entries are retrieved from the
1087 * history database and displayed in the UI.
1089 // Times out on Mac: http://crbug.com/336845
1090 TEST_F('HistoryWebUIIDNTest', 'DISABLED_basic', function() {
1091 // Check that there is only one entry and domain is in unicode.
1092 assertEquals(1, document.querySelectorAll('.domain').length);
1093 assertEquals("\u043f\u0440\u0435\u0437\u0438\u0434\u0435\u043d\u0442." +
1094 "\u0440\u0444", document.querySelector('.domain').textContent);
1096 testDone();
1100 * Fixture for a test that uses the real backend and tests how the history
1101 * page deals with odd schemes in URLs.
1102 * @extends {HistoryWebUIRealBackendTest}
1104 function HistoryWebUIWithSchemesTest() {}
1106 HistoryWebUIWithSchemesTest.prototype = {
1107 __proto__: HistoryWebUIRealBackendTest.prototype,
1109 /** @override */
1110 testGenPreamble: function() {
1111 // Add a bunch of entries on the same day, including some weird schemes.
1112 GEN(' AddPageToHistory(12, "http://google.com", "Google");');
1113 GEN(' AddPageToHistory(13, "file:///tmp/foo", "");');
1114 GEN(' AddPageToHistory(14, "mailto:chromium@chromium.org", "");');
1115 GEN(' AddPageToHistory(15, "tel:555123456", "");');
1118 setUp: function() {
1119 // Show the filter controls as if the command line switch was active.
1120 $('top-container').hidden = true;
1121 $('history-page').classList.add('big-topbar-page');
1122 $('filter-controls').hidden = false;
1123 expectFalse($('filter-controls').hidden);
1127 TEST_F('HistoryWebUIWithSchemesTest', 'groupingWithSchemes', function() {
1128 // Switch to the week view.
1129 $('timeframe-controls').querySelectorAll('input')[1].click();
1130 waitForCallback('historyResult', function() {
1131 // Each URL should be organized under a different "domain".
1132 expectEquals(document.querySelectorAll('.entry').length, 4);
1133 expectEquals(document.querySelectorAll('.site-domain-wrapper').length, 4);
1134 testDone();