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;
11 * Test fixture for history WebUI testing.
13 * @extends {testing.Test}
15 function HistoryUIBrowserTest() {}
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] : '';
29 dateTimeOfDay
: d
.getHours() + ':' + d
.getMinutes(),
30 dateRelativeDay
: d
.toDateString(),
31 allTimestamps
: [timestamp
],
34 title
: d
.toString(), // Use the stringified date as the title.
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
);
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
);
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
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
);
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
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
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}
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',
144 typedefCppFixture
: 'HistoryUIBrowserTest',
147 runAccessibilityChecks
: true,
150 accessibilityIssuesAreErrors
: 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}
163 function HistoryWebUIFakeBackendTest() {
166 HistoryWebUIFakeBackendTest
.prototype = {
167 __proto__
: BaseHistoryWebUITest
.prototype,
170 * Register handlers to stub out calls to the history backend.
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
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
) {
203 'historyResult', { term
: args
[0], finished
: true }, []);
207 function queryHistoryImpl(args
, beginTime
, history
) {
208 var searchText
= args
[0];
209 var offset
= args
[1];
211 var endTime
= args
[3] || Number
.MAX_VALUE
;
212 var maxCount
= args
[4];
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
]);
225 // Advance past all entries newer than the specified end time.
227 // Finished is set from the history database so this behavior may not be
228 // completely identical.
230 while (i
< results
.length
&& results
[i
].time
>= endTime
)
235 while (j
< results
.length
&& results
[j
].time
>= beginTime
)
238 finished
= (j
== results
.length
);
239 results
= results
.slice(i
, j
);
241 results
= results
.slice(i
);
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
);
264 queryStartTime
: queryStartTime
,
265 queryEndTime
: queryEndTime
271 * Fixture for History WebUI testing which returns some fake history results
273 * @extends {HistoryWebUIFakeBackendTest}
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];
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_
);
315 if (range
== HistoryModel
.Range
.WEEK
)
316 var interval
= setQueryTimeInWeeks(offset
, this.today
);
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
343 * @param {string} url
346 removeVisitsToUrl_: function(url
, date
) {
347 var day
= date
.toDateString();
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
);
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.
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
);
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"
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
);
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
);
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
) {
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
);
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 }));
536 shiftClick($('checkbox-4'));
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);
562 checkInterval(checked
.slice(11), 19, 24);
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
);
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());
594 $('search-field').value
= '';
595 $('search-button').click();
596 waitForCallback('historyResult', function() {
597 expectEquals(RESULTS_PER_PAGE
, getResultCount());
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,
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
= [
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
631 var visitInDay
= Math
.floor(d
.getHours() / 6);
632 return 'http://google' + domainSuffixByDay
[day
][visitInDay
] + '.com/' +
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
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
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;
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
);
684 * Checks whether the domains in a day are ordered decreasingly.
685 * @param {Element} element Ordered list containing the grouped domains for a
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
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();
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();
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
));
759 * Fixture for History WebUI tests using the real history backend.
760 * @extends {BaseHistoryWebUITest}
763 function HistoryWebUIRealBackendTest() {}
765 HistoryWebUIRealBackendTest
.prototype = {
766 __proto__
: BaseHistoryWebUITest
.prototype,
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
);
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);
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
);
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
);
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
);
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'));
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'));
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'));
982 * Fixture for History WebUI testing when deletions are prohibited.
983 * @extends {HistoryWebUIRealBackendTest}
986 function HistoryWebUIDeleteProhibitedTest() {}
988 HistoryWebUIDeleteProhibitedTest
.prototype = {
989 __proto__
: HistoryWebUIRealBackendTest
.prototype,
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
);
1018 TEST_F('HistoryWebUIDeleteProhibitedTest', 'atLeastOneFocusable', function() {
1019 var results
= document
.querySelectorAll('#results-display [tabindex="0"]');
1020 expectGE(results
.length
, 1);
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
);
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
);
1065 * Fixture for History WebUI testing IDN.
1066 * @extends {BaseHistoryWebUITest}
1069 function HistoryWebUIIDNTest() {}
1071 HistoryWebUIIDNTest
.prototype = {
1072 __proto__
: BaseHistoryWebUITest
.prototype,
1075 testGenPreamble: function() {
1076 // Add some visits to the history database.
1077 GEN(' AddPageToHistory(0, "http://xn--d1abbgf6aiiy.xn--p1ai/",' +
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
);
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,
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", "");');
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);