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',
150 * Fixture for History WebUI testing which returns some fake history results
151 * to the frontend. Other fixtures that want to stub out calls to the backend
152 * can extend this one.
153 * @extends {BaseHistoryWebUITest}
156 function HistoryWebUIFakeBackendTest() {
159 HistoryWebUIFakeBackendTest
.prototype = {
160 __proto__
: BaseHistoryWebUITest
.prototype,
163 * Register handlers to stub out calls to the history backend.
166 preLoad: function() {
167 this.registerMockHandler_(
168 'queryHistory', this.queryHistoryStub_
.bind(this));
172 * Register a mock handler for a message to the history backend.
173 * @param handlerName The name of the message to mock.
174 * @param handler The mock message handler function.
176 registerMockHandler_: function(handlerName
, handler
) {
177 // Mock4JS doesn't pass in the actual arguments to the stub, but it _will_
178 // pass the original args to the matcher object. SaveMockArguments acts as
179 // a proxy for another matcher, but keeps track of all the arguments it was
181 var savedArgs
= new SaveMockArguments();
183 this.makeAndRegisterMockHandler([handlerName
]);
184 this.mockHandler
.stubs()[handlerName
](savedArgs
.match(ANYTHING
)).will(
185 callFunctionWithSavedArgs(savedArgs
, handler
));
189 * Default stub for the queryHistory message to the history backend.
190 * Simulates an empty history database. Override this to customize this
191 * behavior for particular tests.
192 * @param {Array} arguments The original arguments to queryHistory.
194 queryHistoryStub_: function(args
) {
196 'historyResult', { term
: args
[0], finished
: true }, []);
200 function queryHistoryImpl(args
, beginTime
, history
) {
201 var searchText
= args
[0];
202 var offset
= args
[1];
204 var endTime
= args
[3] || Number
.MAX_VALUE
;
205 var maxCount
= args
[4];
209 for (var k
= 0; k
< history
.length
; k
++) {
210 // Search only by title in this stub.
211 if (history
[k
].title
.indexOf(searchText
) != -1)
212 results
.push(history
[k
]);
218 // Advance past all entries newer than the specified end time.
220 // Finished is set from the history database so this behavior may not be
221 // completely identical.
223 while (i
< results
.length
&& results
[i
].time
>= endTime
)
228 while (j
< results
.length
&& results
[j
].time
>= beginTime
)
231 finished
= (j
== results
.length
);
232 results
= results
.slice(i
, j
);
234 results
= results
.slice(i
);
238 finished
= (maxCount
>= results
.length
);
239 results
= results
.slice(0, maxCount
);
242 var queryStartTime
= '';
243 var queryEndTime
= '';
244 if (results
.length
) {
245 queryStartTime
= results
[results
.length
- 1].dateRelativeDay
;
246 queryEndTime
= results
[0].dateRelativeDay
;
247 } else if (beginTime
) {
248 queryStartTime
= Date(beginTime
);
249 queryEndTime
= Date(endTime
);
257 queryStartTime
: queryStartTime
,
258 queryEndTime
: queryEndTime
264 * Fixture for History WebUI testing which returns some fake history results
266 * @extends {HistoryWebUIFakeBackendTest}
269 function HistoryWebUITest() {}
271 HistoryWebUITest
.prototype = {
272 __proto__
: HistoryWebUIFakeBackendTest
.prototype,
274 preLoad: function() {
275 HistoryWebUIFakeBackendTest
.prototype.preLoad
.call(this);
277 this.registerMockHandler_(
278 'removeVisits', this.removeVisitsStub_
.bind(this));
280 // Prepare a list of fake history results. The entries will begin at
281 // 1:00 AM on Sept 2, 2008, and will be spaced two minutes apart.
282 var timestamp
= new Date(2008, 9, 2, 1, 0).getTime();
283 this.fakeHistory_
= [];
285 for (var i
= 0; i
< TOTAL_RESULT_COUNT
; i
++) {
286 this.fakeHistory_
.push(
287 createHistoryEntry(timestamp
, 'http://google.com/' + timestamp
));
288 timestamp
-= 2 * 60 * 1000; // Next visit is two minutes earlier.
293 * Stub for the 'queryHistory' message to the history backend.
294 * Simulates a history database using the fake history data that is
295 * initialized in preLoad().
296 * @param {Array} arguments The original arguments to queryHistory.
298 queryHistoryStub_: function(args
) {
299 var searchText
= args
[0];
300 var offset
= args
[1];
302 var endTime
= args
[3] || Number
.MAX_VALUE
;
303 var maxCount
= args
[4];
304 if (range
== HistoryModel
.Range
.ALL_TIME
) {
305 queryHistoryImpl(args
, null, this.fakeHistory_
);
308 if (range
== HistoryModel
.Range
.WEEK
)
309 var interval
= setQueryTimeInWeeks(offset
, this.today
);
311 var interval
= setQueryTimeInMonths(offset
, this.today
);
313 args
[3] = interval
.endTime
.getTime();
314 queryHistoryImpl(args
, interval
.beginTime
.getTime(), this.fakeHistory_
);
318 * Stub for the 'removeVisits' message to the history backend.
319 * This will modify the fake history data in the test instance, so that
320 * further 'queryHistory' messages will not contain the deleted entries.
321 * @param {Array} arguments The original arguments to removeVisits.
323 removeVisitsStub_: function(args
) {
324 for (var i
= 0; i
< args
.length
; ++i
) {
325 var url
= args
[i
].url
;
326 var timestamps
= args
[i
].timestamps
;
327 assertEquals(timestamps
.length
, 1);
328 this.removeVisitsToUrl_(url
, new Date(timestamps
[0]));
330 callFrontendAsync('deleteComplete');
334 * Removes any visits to |url| on the same day as |date| from the fake
336 * @param {string} url
339 removeVisitsToUrl_: function(url
, date
) {
340 var day
= date
.toDateString();
342 for (var i
= 0, visit
; visit
= this.fakeHistory_
[i
]; ++i
) {
343 if (url
!= visit
.url
|| visit
.dateRelativeDay
!= day
)
344 newHistory
.push(visit
);
346 this.fakeHistory_
= newHistory
;
351 * Examines the time column of every entry on the page, and ensure that they
352 * are all the same width.
354 function ensureTimeWidthsEqual() {
355 var times
= document
.querySelectorAll('.entry .time');
356 var timeWidth
= times
[0].clientWidth
;
357 for (var i
= 1; i
< times
.length
; ++i
) {
358 assertEquals(timeWidth
, times
[i
].clientWidth
);
362 TEST_F('HistoryWebUIFakeBackendTest', 'emptyHistory', function() {
363 expectTrue($('newest-button').hidden
);
364 expectTrue($('newer-button').hidden
);
365 expectTrue($('older-button').hidden
);
369 // Times out on Win: http://crbug.com/336845
370 TEST_F('HistoryWebUITest', 'DISABLED_basicTest', function() {
371 var resultCount
= document
.querySelectorAll('.entry').length
;
373 // Check that there are two days of entries.
374 var dayHeaders
= document
.querySelectorAll('.day');
375 assertEquals(2, dayHeaders
.length
);
376 expectNotEquals(dayHeaders
[0].textContent
, dayHeaders
[1].textContent
);
378 // Check that the entries in each day are time-ordered, and that no
379 // duplicate URLs appear on a given day.
381 var lastDate
= new Date();
382 for (var day
= 0; day
< dayHeaders
.length
; ++day
) {
383 var dayTitle
= dayHeaders
[day
].textContent
;
384 var dayResults
= document
.querySelectorAll('.day-results')[day
];
385 var entries
= dayResults
.querySelectorAll('.entry');
386 expectGT(entries
.length
, 0);
388 for (var i
= 0, entry
; entry
= entries
[i
]; ++i
) {
389 var time
= entry
.querySelector('.time').textContent
;
390 expectGT(time
.length
, 0);
392 var date
= new Date(dayTitle
+ ' ' + time
);
393 expectGT(lastDate
, date
);
396 // Ensure it's not a duplicate URL for this day.
397 var dayAndUrl
= day
+ entry
.querySelector('a').href
;
398 expectFalse(urlsByDay
.hasOwnProperty(dayAndUrl
));
399 urlsByDay
[dayAndUrl
] = dayAndUrl
;
401 // Reconstruct the entry date from the title, and ensure that it's
402 // consistent with the date header and with the time.
403 var entryDate
= new Date(entry
.querySelector('.title').textContent
);
404 expectEquals(entryDate
.getYear(), date
.getYear());
405 expectEquals(entryDate
.getMonth(), date
.getMonth());
406 expectEquals(entryDate
.getDay(), date
.getDay());
407 expectEquals(entryDate
.getHours(), date
.getHours());
408 expectEquals(entryDate
.getMinutes(), date
.getMinutes());
412 // Check that there are 3 page navigation links and that only the "Older"
414 expectEquals(3, document
.querySelectorAll('.link-button').length
);
415 expectTrue($('newest-button').hidden
);
416 expectTrue($('newer-button').hidden
);
417 expectFalse($('older-button').hidden
);
419 ensureTimeWidthsEqual();
421 // Go to the next page.
422 $('older-button').click();
423 waitForCallback('historyResult', function() {
424 resultCount
+= document
.querySelectorAll('.entry').length
;
426 // Check that the two pages include all of the entries.
427 expectEquals(TOTAL_RESULT_COUNT
, resultCount
);
429 // Check that the day header was properly continued -- the header for the
430 // last day on the first page should be a substring of the header on the
431 // second page. E.g. "Wed, Oct 8, 2008" and "Web, Oct 8, 2008 - cont'd".
432 var newDayHeaders
= document
.querySelectorAll('.day');
433 expectEquals(1, newDayHeaders
.length
);
435 newDayHeaders
[0].textContent
.indexOf(dayHeaders
[1].textContent
));
437 // Check that the "Newest" and "Newer" links are now visible, but the
438 // "Older" link is hidden.
439 expectEquals(3, document
.querySelectorAll('.link-button').length
);
440 expectFalse($('newest-button').hidden
);
441 expectFalse($('newer-button').hidden
);
442 expectTrue($('older-button').hidden
);
444 ensureTimeWidthsEqual();
446 // Go back to the first page, and check that the same day headers are there.
447 $('newest-button').click();
448 var newDayHeaders
= document
.querySelectorAll('.day');
449 expectEquals(2, newDayHeaders
.length
);
451 expectNotEquals(newDayHeaders
[0].textContent
,
452 newDayHeaders
[1].textContent
);
453 expectEquals(dayHeaders
[0].textContent
, newDayHeaders
[0].textContent
);
454 expectEquals(dayHeaders
[1].textContent
, newDayHeaders
[1].textContent
);
461 * Test bulk deletion of history entries.
462 * Disabled because it is currently very flaky on the Windows XP bot.
464 TEST_F('HistoryWebUITest', 'DISABLED_bulkDeletion', function() {
465 var checkboxes
= document
.querySelectorAll(
466 '#results-display input[type=checkbox]');
468 // Immediately confirm the history deletion.
469 confirmDeletion = function(okCallback
, cancelCallback
) {
473 // The "remove" button should be initially disabled.
474 var removeButton
= $('remove-selected');
475 expectTrue(removeButton
.disabled
);
477 checkboxes
[0].click();
478 expectFalse(removeButton
.disabled
);
480 var firstEntry
= document
.querySelector('.title a').textContent
;
481 removeButton
.click();
483 // After deletion, expect the results to be reloaded.
484 waitForCallback('historyResult', function() {
485 expectNotEquals(document
.querySelector('.title a').textContent
, firstEntry
);
486 expectTrue(removeButton
.disabled
);
488 // Delete the first 3 entries.
489 checkboxes
= document
.querySelectorAll(
490 '#results-display input[type=checkbox]');
491 checkboxes
[0].click();
492 checkboxes
[1].click();
493 checkboxes
[2].click();
494 expectFalse(removeButton
.disabled
);
496 var nextEntry
= document
.querySelectorAll('.title a')[3];
497 removeButton
.click();
498 waitForCallback('historyResult', function() {
499 // The next entry after the deleted ones should now be the first.
500 expectEquals(document
.querySelector('.title a').textContent
,
501 nextEntry
.textContent
);
508 * Test selecting multiple entries using shift click.
509 * Disabled due to time out on all platforms: crbug/375910
511 TEST_F('HistoryWebUITest', 'DISABLED_multipleSelect', function() {
512 var checkboxes
= document
.querySelectorAll(
513 '#results-display input[type=checkbox]');
515 var getAllChecked = function() {
516 return Array
.prototype.slice
.call(document
.querySelectorAll(
517 '#results-display input[type=checkbox]:checked'));
520 // Make sure that nothing is checked.
521 expectEquals(0, getAllChecked().length
);
523 var shiftClick = function(el
) {
524 el
.dispatchEvent(new MouseEvent('click', { shiftKey
: true }));
528 shiftClick($('checkbox-4'));
530 shiftClick($('checkbox-9'));
532 // See if they are checked.
533 var checked
= getAllChecked();
534 expectEquals(6, checked
.length
);
535 checkInterval(checked
, 4, 9);
537 // Extend the selection.
538 shiftClick($('checkbox-14'));
540 checked
= getAllChecked();
541 expectEquals(11, checked
.length
);
542 checkInterval(checked
, 4, 14);
544 // Now do a normal click on a higher ID box and a shift click on a lower ID
545 // one (test the other way around).
546 $('checkbox-24').click();
547 shiftClick($('checkbox-19'));
549 checked
= getAllChecked();
550 expectEquals(17, checked
.length
);
551 // First set of checkboxes (11).
552 checkInterval(checked
, 4, 14);
554 checkInterval(checked
.slice(11), 19, 24);
557 $('checkbox-26').click();
558 shiftClick($('checkbox-20'));
560 checked
= getAllChecked();
561 // checkbox-20 to checkbox-24 should be deselected now.
562 expectEquals(12, checked
.length
);
563 // First set of checkboxes (11).
564 checkInterval(checked
, 4, 14);
565 // Only checkbox-19 should still be selected.
566 expectEquals('checkbox-19', checked
[11].id
);
571 TEST_F('HistoryWebUITest', 'DISABLED_searchHistory', function() {
572 var getResultCount = function() {
573 return document
.querySelectorAll('.entry').length
;
575 // See that all the elements are there.
576 expectEquals(RESULTS_PER_PAGE
, getResultCount());
578 // See that the search works.
579 $('search-field').value
= 'Thu Oct 02 2008';
580 $('search-button').click();
582 waitForCallback('historyResult', function() {
583 expectEquals(31, getResultCount());
586 $('search-field').value
= '';
587 $('search-button').click();
588 waitForCallback('historyResult', function() {
589 expectEquals(RESULTS_PER_PAGE
, getResultCount());
595 function setPageState(searchText
, page
, groupByDomain
, range
, offset
) {
596 window
.location
= '#' + PageState
.getHashString(
597 searchText
, page
, groupByDomain
, range
, offset
);
600 function RangeHistoryWebUITest() {}
602 RangeHistoryWebUITest
.prototype = {
603 __proto__
: HistoryWebUITest
.prototype,
606 preLoad: function() {
607 HistoryWebUITest
.prototype.preLoad
.call(this);
608 // Repeat the domain visits every 4 days. The nested lists contain the
609 // domain suffixes for the visits in a day.
610 var domainSuffixByDay
= [
617 var buildDomainUrl = function(timestamp
) {
618 var d
= new Date(timestamp
);
619 // Repeat the same setup of domains every 4 days.
620 var day
= d
.getDate() % 4;
621 // Assign an entry for every 6 hours so that we get 4 entries per day
623 var visitInDay
= Math
.floor(d
.getHours() / 6);
624 return 'http://google' + domainSuffixByDay
[day
][visitInDay
] + '.com/' +
628 // Prepare a list of fake history results. Start the results on
629 // 11:00 PM on May 2, 2012 and add 4 results every day (one result every 6
631 var timestamp
= new Date(2012, 4, 2, 23, 0).getTime();
632 this.today
= new Date(2012, 4, 2);
633 this.fakeHistory_
= [];
635 // Put in 2 days for May and 30 days for April so the results span over
637 for (var i
= 0; i
< 4 * 32; i
++) {
638 this.fakeHistory_
.push(
639 createHistoryEntry(timestamp
, buildDomainUrl(timestamp
)));
640 timestamp
-= 6 * 60 * 60 * 1000;
643 // Leave March empty.
644 timestamp
-= 31 * 24 * 3600 * 1000;
646 // Put results in February.
647 for (var i
= 0; i
< 29 * 4; i
++) {
648 this.fakeHistory_
.push(
649 createHistoryEntry(timestamp
, buildDomainUrl(timestamp
)));
650 timestamp
-= 6 * 60 * 60 * 1000;
655 // Show the filter controls as if the command line switch was active.
656 $('top-container').hidden
= true;
657 $('history-page').classList
.add('big-topbar-page');
658 $('filter-controls').hidden
= false;
659 expectFalse($('filter-controls').hidden
);
664 * Disabled due intermitent failures on multiple OSes http://crbug.com/377338
666 TEST_F('RangeHistoryWebUITest', 'DISABLED_allView', function() {
667 // Check that we start off in the all time view.
668 expectTrue($('timeframe-filter-all').checked
);
669 // See if the correct number of days is shown.
670 var dayHeaders
= document
.querySelectorAll('.day');
671 assertEquals(Math
.ceil(RESULTS_PER_PAGE
/ 4), dayHeaders
.length
);
676 * Checks whether the domains in a day are ordered decreasingly.
677 * @param {Element} element Ordered list containing the grouped domains for a
680 function checkGroupedVisits(element
) {
681 // The history page contains the number of visits next to a domain in
682 // parentheses (e.g. 'google.com (5)'). This function extracts that number
684 var getNumberVisits = function(element
) {
685 return parseInt(element
.textContent
.replace(/\D/g, ''), 10);
688 // Read the number of visits from each domain and make sure that it is lower
689 // than or equal to the number of visits from the previous domain.
690 var domainEntries
= element
.querySelectorAll('.number-visits');
691 var currentNumberOfVisits
= getNumberVisits(domainEntries
[0]);
692 for (var j
= 1; j
< domainEntries
.length
; j
++) {
693 var numberOfVisits
= getNumberVisits(domainEntries
[j
]);
694 assertTrue(currentNumberOfVisits
>= numberOfVisits
);
695 currentNumberOfVisits
= numberOfVisits
;
699 TEST_F('RangeHistoryWebUITest', 'weekViewGrouped', function() {
700 // Change to weekly view.
701 setPageState('', 0, HistoryModel
.Range
.WEEK
, 0);
702 waitForCallback('historyResult', function() {
703 // See if the correct number of days is still shown.
704 var dayResults
= document
.querySelectorAll('.day-results');
705 assertEquals(7, dayResults
.length
);
707 // Check whether the results are ordered by visits.
708 for (var i
= 0; i
< dayResults
.length
; i
++)
709 checkGroupedVisits(dayResults
[i
]);
711 ensureTimeWidthsEqual();
717 TEST_F('RangeHistoryWebUITest', 'monthViewGrouped', function() {
718 // Change to monthly view.
719 setPageState('', 0, HistoryModel
.Range
.MONTH
, 0);
720 waitForCallback('historyResult', function() {
721 // See if the correct number of days is shown.
722 var monthResults
= document
.querySelectorAll('.month-results');
723 assertEquals(1, monthResults
.length
);
725 checkGroupedVisits(monthResults
[0]);
726 ensureTimeWidthsEqual();
732 TEST_F('RangeHistoryWebUITest', 'monthViewEmptyMonth', function() {
733 // Change to monthly view.
734 setPageState('', 0, HistoryModel
.Range
.MONTH
, 2);
736 waitForCallback('historyResult', function() {
737 // See if the correct number of days is shown.
738 var resultsDisplay
= $('results-display');
739 assertEquals(0, resultsDisplay
.querySelectorAll('.months-results').length
);
740 assertEquals(1, resultsDisplay
.querySelectorAll('div').length
);
747 * Fixture for History WebUI tests using the real history backend.
748 * @extends {BaseHistoryWebUITest}
751 function HistoryWebUIRealBackendTest() {}
753 HistoryWebUIRealBackendTest
.prototype = {
754 __proto__
: BaseHistoryWebUITest
.prototype,
757 testGenPreamble: function() {
758 // Add some visits to the history database.
759 GEN(' AddPageToHistory(0, "http://google.com", "Google");');
760 GEN(' AddPageToHistory(1, "http://example.com", "Example");');
761 GEN(' AddPageToHistory(2, "http://google.com", "Google");');
763 // Add a visit on the next day.
764 GEN(' AddPageToHistory(36, "http://google.com", "Google");');
769 * Simple test that verifies that the correct entries are retrieved from the
770 * history database and displayed in the UI.
772 TEST_F('HistoryWebUIRealBackendTest', 'basic', function() {
773 // Check that there are two days of entries, and three entries in total.
774 assertEquals(2, document
.querySelectorAll('.day').length
);
775 assertEquals(3, document
.querySelectorAll('.entry').length
);
780 TEST_F('HistoryWebUIRealBackendTest', 'atLeastOneFocusable', function() {
781 expectEquals(1, document
.querySelectorAll('[tabindex="0"]').length
);
785 TEST_F('HistoryWebUIRealBackendTest', 'deleteRemovesEntry', function() {
786 assertTrue(historyModel
.deletingHistoryAllowed
);
788 var visit
= document
.querySelector('.entry').visit
;
789 visit
.titleLink
.focus();
790 assertEquals(visit
.titleLink
, document
.activeElement
);
792 var deleteKey
= document
.createEvent('KeyboardEvent');
793 deleteKey
.initKeyboardEvent('keydown', true, true, window
, 'U+007F');
794 assertEquals('U+007F', deleteKey
.keyIdentifier
);
796 assertFalse(historyModel
.isDeletingVisits());
797 expectFalse(visit
.titleLink
.dispatchEvent(deleteKey
));
798 expectTrue(historyModel
.isDeletingVisits());
800 expectNotEquals(visit
.dropDown
, document
.activeElement
);
805 * Test individual deletion of history entries.
807 TEST_F('HistoryWebUIRealBackendTest', 'singleDeletion', function() {
808 // Deletes the history entry represented by |entryElement|, and calls callback
809 // when the deletion is complete.
810 var removeEntry = function(entryElement
, callback
) {
811 var dropDownButton
= entryElement
.querySelector('.drop-down');
812 var removeMenuItem
= $('remove-visit');
814 assertFalse(dropDownButton
.disabled
);
815 assertFalse(removeMenuItem
.disabled
);
817 waitForCallback('onEntryRemoved', callback
);
819 cr
.dispatchSimpleEvent(dropDownButton
, 'mousedown');
820 cr
.dispatchSimpleEvent(removeMenuItem
, 'activate');
823 var secondTitle
= document
.querySelectorAll('.entry a')[1].textContent
;
824 var thirdTitle
= document
.querySelectorAll('.entry a')[2].textContent
;
826 // historyDeleted() should not be called when deleting individual entries
827 // using the drop down.
828 waitForCallback('historyDeleted', function() {
829 testDone([false, 'historyDeleted() called when deleting single entry']);
832 expectEquals(2, document
.querySelectorAll('.day').length
);
834 // Delete the first entry. The previous second entry should now be the first.
835 removeEntry(document
.querySelector('.entry'), function() {
836 expectEquals(secondTitle
, document
.querySelector('.entry a').textContent
);
838 // After removing the first entry, its day header should also be gone.
839 expectEquals(1, document
.querySelectorAll('.day').length
);
841 // Delete another entry. The original third entry should now be the first.
842 removeEntry(document
.querySelector('.entry'), function() {
843 expectEquals(thirdTitle
, document
.querySelector('.entry a').textContent
);
849 TEST_F('HistoryWebUIRealBackendTest', 'leftRightChangeFocus', function() {
850 var visit
= document
.querySelector('.entry').visit
;
851 visit
.titleLink
.focus();
852 assertEquals(visit
.titleLink
, document
.activeElement
);
854 var right
= document
.createEvent('KeyboardEvent');
855 right
.initKeyboardEvent('keydown', true, true, window
, 'Right');
856 assertEquals('Right', right
.keyIdentifier
);
857 expectFalse(visit
.titleLink
.dispatchEvent(right
));
859 assertEquals(visit
.dropDown
, document
.activeElement
);
861 var left
= document
.createEvent('KeyboardEvent');
862 left
.initKeyboardEvent('keydown', true, true, window
, 'Left');
863 assertEquals('Left', left
.keyIdentifier
);
864 expectFalse(visit
.dropDown
.dispatchEvent(left
));
866 expectEquals(visit
.titleLink
, document
.activeElement
);
871 * Fixture for History WebUI testing when deletions are prohibited.
872 * @extends {HistoryWebUIRealBackendTest}
875 function HistoryWebUIDeleteProhibitedTest() {}
877 HistoryWebUIDeleteProhibitedTest
.prototype = {
878 __proto__
: HistoryWebUIRealBackendTest
.prototype,
881 testGenPreamble: function() {
882 HistoryWebUIRealBackendTest
.prototype.testGenPreamble
.call(this);
883 GEN(' SetDeleteAllowed(false);');
887 // Test UI when removing entries is prohibited.
888 TEST_F('HistoryWebUIDeleteProhibitedTest', 'deleteProhibited', function() {
889 // No checkboxes should be created.
890 var checkboxes
= document
.querySelectorAll(
891 '#results-display input[type=checkbox]');
892 expectEquals(0, checkboxes
.length
);
894 // The "remove" button should be disabled.
895 var removeButton
= $('remove-selected');
896 expectTrue(removeButton
.disabled
);
898 // The "Remove from history" drop-down item should be disabled.
899 var removeVisit
= $('remove-visit');
900 expectTrue(removeVisit
.disabled
);
905 TEST_F('HistoryWebUIDeleteProhibitedTest', 'atLeastOneFocusable', function() {
906 expectEquals(1, document
.querySelectorAll('[tabindex="0"]').length
);
910 TEST_F('HistoryWebUIDeleteProhibitedTest', 'leftRightChangeFocus', function() {
911 var visit
= document
.querySelector('.entry').visit
;
912 visit
.titleLink
.focus();
913 assertEquals(visit
.titleLink
, document
.activeElement
);
915 var right
= document
.createEvent('KeyboardEvent');
916 right
.initKeyboardEvent('keydown', true, true, window
, 'Right');
917 assertEquals('Right', right
.keyIdentifier
);
918 expectFalse(visit
.titleLink
.dispatchEvent(right
));
920 assertEquals(visit
.dropDown
, document
.activeElement
);
922 var left
= document
.createEvent('KeyboardEvent');
923 left
.initKeyboardEvent('keydown', true, true, window
, 'Left');
924 assertEquals('Left', left
.keyIdentifier
);
925 expectFalse(visit
.dropDown
.dispatchEvent(left
));
927 expectEquals(visit
.titleLink
, document
.activeElement
);
931 TEST_F('HistoryWebUIDeleteProhibitedTest', 'deleteIgnored', function() {
932 assertFalse(historyModel
.deletingHistoryAllowed
);
934 var visit
= document
.querySelector('.entry').visit
;
935 visit
.titleLink
.focus();
936 assertEquals(visit
.titleLink
, document
.activeElement
);
938 var deleteKey
= document
.createEvent('KeyboardEvent');
939 deleteKey
.initKeyboardEvent('keydown', true, true, window
, 'U+007F');
940 assertEquals('U+007F', deleteKey
.keyIdentifier
);
942 assertFalse(historyModel
.isDeletingVisits());
943 expectTrue(visit
.titleLink
.dispatchEvent(deleteKey
));
944 expectFalse(historyModel
.isDeletingVisits());
946 expectEquals(visit
.titleLink
, document
.activeElement
);
951 * Fixture for History WebUI testing IDN.
952 * @extends {BaseHistoryWebUITest}
955 function HistoryWebUIIDNTest() {}
957 HistoryWebUIIDNTest
.prototype = {
958 __proto__
: BaseHistoryWebUITest
.prototype,
961 testGenPreamble: function() {
962 // Add some visits to the history database.
963 GEN(' AddPageToHistory(0, "http://xn--d1abbgf6aiiy.xn--p1ai/",' +
966 // Clear AcceptLanguages to get domain in unicode.
967 GEN(' ClearAcceptLanguages();');
972 * Simple test that verifies that the correct entries are retrieved from the
973 * history database and displayed in the UI.
975 TEST_F('HistoryWebUIIDNTest', 'basic', function() {
976 // Check that there is only one entry and domain is in unicode.
977 assertEquals(1, document
.querySelectorAll('.domain').length
);
978 assertEquals("\u043f\u0440\u0435\u0437\u0438\u0434\u0435\u043d\u0442." +
979 "\u0440\u0444", document
.querySelector('.domain').textContent
);
985 * Fixture for a test that uses the real backend and tests how the history
986 * page deals with odd schemes in URLs.
987 * @extends {HistoryWebUIRealBackendTest}
989 function HistoryWebUIWithSchemesTest() {}
991 HistoryWebUIWithSchemesTest
.prototype = {
992 __proto__
: HistoryWebUIRealBackendTest
.prototype,
995 testGenPreamble: function() {
996 // Add a bunch of entries on the same day, including some weird schemes.
997 GEN(' AddPageToHistory(12, "http://google.com", "Google");');
998 GEN(' AddPageToHistory(13, "file:///tmp/foo", "");');
999 GEN(' AddPageToHistory(14, "mailto:chromium@chromium.org", "");');
1000 GEN(' AddPageToHistory(15, "tel:555123456", "");');
1004 // Show the filter controls as if the command line switch was active.
1005 $('top-container').hidden
= true;
1006 $('history-page').classList
.add('big-topbar-page');
1007 $('filter-controls').hidden
= false;
1008 expectFalse($('filter-controls').hidden
);
1012 TEST_F('HistoryWebUIWithSchemesTest', 'groupingWithSchemes', function() {
1013 // Switch to the week view.
1014 $('timeframe-filter-week').click();
1015 waitForCallback('historyResult', function() {
1016 // Each URL should be organized under a different "domain".
1017 expectEquals(document
.querySelectorAll('.entry').length
, 4);
1018 expectEquals(document
.querySelectorAll('.site-domain-wrapper').length
, 4);