| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| GEN('#include "chrome/test/data/webui/history_ui_browsertest.h"'); |
| |
| /** @const */ var TOTAL_RESULT_COUNT = 160; |
| /** @const */ var WAIT_TIMEOUT = 200; |
| |
| /** |
| * Test fixture for history WebUI testing. |
| * @constructor |
| * @extends {testing.Test} |
| */ |
| function HistoryUIBrowserTest() {} |
| |
| /** |
| * Create a fake history result with the given timestamp. |
| * @param {Number} timestamp Timestamp of the entry, in ms since the epoch. |
| * @param {String} url The URL to set on this entry. |
| * @return {Object} An object representing a history entry. |
| */ |
| function createHistoryEntry(timestamp, url) { |
| var d = new Date(timestamp); |
| // Extract domain from url. |
| var domainMatch = url.replace(/^.+?:\/\//, '').match(/[^/]+/); |
| var domain = domainMatch ? domainMatch[0] : ''; |
| return { |
| dateTimeOfDay: d.getHours() + ':' + d.getMinutes(), |
| dateRelativeDay: d.toDateString(), |
| allTimestamps: [timestamp], |
| starred: false, |
| time: timestamp, |
| title: d.toString(), // Use the stringified date as the title. |
| url: url, |
| domain: domain |
| }; |
| } |
| |
| /** |
| * Wait for the history backend to call the global function named |
| * |callbackName|, and then execute |afterFunction|. This allows tests to |
| * wait on asynchronous backend operations before proceeding. |
| */ |
| function waitForCallback(callbackName, afterFunction) { |
| var originalCallback = window[callbackName]; |
| |
| // Install a wrapper that temporarily replaces the original function. |
| window[callbackName] = function() { |
| window[callbackName] = originalCallback; |
| originalCallback.apply(this, arguments); |
| afterFunction(); |
| }; |
| } |
| |
| /** |
| * Asynchronously execute the global function named |functionName|. This |
| * should be used for all calls from backend stubs to the frontend. |
| */ |
| function callFrontendAsync(functionName) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| setTimeout(function() { |
| window[functionName].apply(window, args); |
| }, 1); |
| } |
| |
| /** |
| * Checks that all the checkboxes in the [|start|, |end|] interval are checked |
| * and that their IDs are properly set. Does that against the checkboxes in |
| * |checked|, starting from the |startInChecked| position. |
| * @param {Array} checked An array of all the relevant checked checkboxes |
| * on this page. |
| * @param {Number} start The starting checkbox id. |
| * @param {Number} end The ending checkbox id. |
| */ |
| function checkInterval(checked, start, end) { |
| for (var i = start; i <= end; i++) |
| expectEquals('checkbox-' + i, checked[i - start].id); |
| } |
| |
| /** |
| * Returns a period of 7 days, |offset| weeks back from |today|. The behavior |
| * of this function should be identical to |
| * BrowsingHistoryHandler::SetQueryTimeInWeeks. |
| * @param {Number} offset Number of weeks to go back. |
| * @param {Date} today Which date to consider as "today" (since we're not using |
| * the actual current date in this case). |
| * @return {Object} An object containing the begin date and the end date of the |
| * computed period. |
| */ |
| function setQueryTimeInWeeks(offset, today) { |
| // Going back one day at a time starting from midnight will make sure that |
| // the other values get updated properly. |
| var endTime = new Date(today); |
| endTime.setHours(24, 0, 0, 0); |
| for (var i = 0; i < 7 * offset; i++) |
| endTime.setDate(endTime.getDate() - 1); |
| var beginTime = new Date(endTime); |
| for (var i = 0; i < 7; i++) |
| beginTime.setDate(beginTime.getDate() - 1); |
| return {'endTime': endTime, 'beginTime': beginTime}; |
| } |
| |
| /** |
| * Returns the period of a month, |offset| months back from |today|. The |
| * behavior of this function should be identical to |
| * BrowsingHistoryHandler::SetQueryTimeInMonths. |
| * @param {Number} offset Number of months to go back. |
| * @param {Date} today Which date to consider as "today" (since we're not using |
| * the actual current date in this case). |
| * @return {Object} An object containing the begin date and the end date of the |
| * computed period. |
| */ |
| function setQueryTimeInMonths(offset, today) { |
| var endTime = new Date(today); |
| var beginTime = new Date(today); |
| // Last day of this month. |
| endTime.setMonth(endTime.getMonth() + 1, 0); |
| // First day of the current month. |
| beginTime.setMonth(beginTime.getMonth(), 1); |
| for (var i = 0; i < offset; i++) { |
| beginTime.setMonth(beginTime.getMonth() - 1); |
| endTime.setMonth(endTime.getMonth() - 1); |
| } |
| return {'endTime': endTime, 'beginTime': beginTime}; |
| } |
| |
| /** |
| * Base fixture for History WebUI testing. |
| * @extends {testing.Test} |
| * @constructor |
| */ |
| function BaseHistoryWebUITest() {} |
| |
| BaseHistoryWebUITest.prototype = { |
| __proto__: testing.Test.prototype, |
| |
| /** |
| * Browse to the history page & call our preLoad(). |
| */ |
| browsePreload: 'chrome://history-frame', |
| |
| /** @override */ |
| typedefCppFixture: 'HistoryUIBrowserTest', |
| |
| /** @override */ |
| runAccessibilityChecks: true, |
| |
| /** @override */ |
| accessibilityIssuesAreErrors: true, |
| |
| /** @override */ |
| isAsync: true, |
| }; |
| |
| /** |
| * Fixture for History WebUI testing which returns some fake history results |
| * to the frontend. Other fixtures that want to stub out calls to the backend |
| * can extend this one. |
| * @extends {BaseHistoryWebUITest} |
| * @constructor |
| */ |
| function HistoryWebUIFakeBackendTest() { |
| } |
| |
| HistoryWebUIFakeBackendTest.prototype = { |
| __proto__: BaseHistoryWebUITest.prototype, |
| |
| /** |
| * Register handlers to stub out calls to the history backend. |
| * @override |
| */ |
| preLoad: function() { |
| this.registerMockHandler_( |
| 'queryHistory', this.queryHistoryStub_.bind(this)); |
| }, |
| |
| /** |
| * Register a mock handler for a message to the history backend. |
| * @param handlerName The name of the message to mock. |
| * @param handler The mock message handler function. |
| */ |
| registerMockHandler_: function(handlerName, handler) { |
| // Mock4JS doesn't pass in the actual arguments to the stub, but it _will_ |
| // pass the original args to the matcher object. SaveMockArguments acts as |
| // a proxy for another matcher, but keeps track of all the arguments it was |
| // asked to match. |
| var savedArgs = new SaveMockArguments(); |
| |
| this.makeAndRegisterMockHandler([handlerName]); |
| this.mockHandler.stubs()[handlerName](savedArgs.match(ANYTHING)).will( |
| callFunctionWithSavedArgs(savedArgs, handler)); |
| }, |
| |
| /** |
| * Default stub for the queryHistory message to the history backend. |
| * Simulates an empty history database. Override this to customize this |
| * behavior for particular tests. |
| * @param {Array} arguments The original arguments to queryHistory. |
| */ |
| queryHistoryStub_: function(args) { |
| callFrontendAsync( |
| 'historyResult', { term: args[0], finished: true }, []); |
| } |
| }; |
| |
| function queryHistoryImpl(args, beginTime, history) { |
| var searchText = args[0]; |
| var offset = args[1]; |
| var range = args[2]; |
| var endTime = args[3] || Number.MAX_VALUE; |
| var maxCount = args[4]; |
| |
| var results = []; |
| if (searchText) { |
| for (var k = 0; k < history.length; k++) { |
| // Search only by title in this stub. |
| if (history[k].title.indexOf(searchText) != -1) |
| results.push(history[k]); |
| } |
| } else { |
| results = history; |
| } |
| |
| // Advance past all entries newer than the specified end time. |
| var i = 0; |
| // Finished is set from the history database so this behavior may not be |
| // completely identical. |
| var finished = true; |
| while (i < results.length && results[i].time >= endTime) |
| ++i; |
| |
| if (beginTime) { |
| var j = i; |
| while (j < results.length && results[j].time >= beginTime) |
| ++j; |
| |
| finished = (j == results.length); |
| results = results.slice(i, j); |
| } else { |
| results = results.slice(i); |
| } |
| |
| if (maxCount) { |
| finished = (maxCount >= results.length); |
| results = results.slice(0, maxCount); |
| } |
| |
| var queryStartTime = ''; |
| var queryEndTime = ''; |
| if (results.length) { |
| queryStartTime = results[results.length - 1].dateRelativeDay; |
| queryEndTime = results[0].dateRelativeDay; |
| } else if (beginTime) { |
| queryStartTime = Date(beginTime); |
| queryEndTime = Date(endTime); |
| } |
| |
| callFrontendAsync( |
| 'historyResult', |
| { |
| term: searchText, |
| finished: finished, |
| queryStartTime: queryStartTime, |
| queryEndTime: queryEndTime |
| }, |
| results); |
| } |
| |
| /** |
| * Fixture for History WebUI testing which returns some fake history results |
| * to the frontend. |
| * @extends {HistoryWebUIFakeBackendTest} |
| * @constructor |
| */ |
| function HistoryWebUITest() {} |
| |
| HistoryWebUITest.prototype = { |
| __proto__: HistoryWebUIFakeBackendTest.prototype, |
| |
| preLoad: function() { |
| HistoryWebUIFakeBackendTest.prototype.preLoad.call(this); |
| |
| this.registerMockHandler_( |
| 'removeVisits', this.removeVisitsStub_.bind(this)); |
| |
| // Prepare a list of fake history results. The entries will begin at |
| // 1:00 AM on Sept 2, 2008, and will be spaced two minutes apart. |
| var timestamp = new Date(2008, 9, 2, 1, 0).getTime(); |
| this.fakeHistory_ = []; |
| |
| for (var i = 0; i < TOTAL_RESULT_COUNT; i++) { |
| this.fakeHistory_.push( |
| createHistoryEntry(timestamp, 'http://google.com/' + timestamp)); |
| timestamp -= 2 * 60 * 1000; // Next visit is two minutes earlier. |
| } |
| }, |
| |
| /** |
| * Stub for the 'queryHistory' message to the history backend. |
| * Simulates a history database using the fake history data that is |
| * initialized in preLoad(). |
| * @param {Array} arguments The original arguments to queryHistory. |
| */ |
| queryHistoryStub_: function(args) { |
| var searchText = args[0]; |
| var offset = args[1]; |
| var range = args[2]; |
| var endTime = args[3] || Number.MAX_VALUE; |
| var maxCount = args[4]; |
| if (range == HistoryModel.Range.ALL_TIME) { |
| queryHistoryImpl(args, null, this.fakeHistory_); |
| return; |
| } |
| if (range == HistoryModel.Range.WEEK) |
| var interval = setQueryTimeInWeeks(offset, this.today); |
| else |
| var interval = setQueryTimeInMonths(offset, this.today); |
| |
| args[3] = interval.endTime.getTime(); |
| queryHistoryImpl(args, interval.beginTime.getTime(), this.fakeHistory_); |
| }, |
| |
| /** |
| * Stub for the 'removeVisits' message to the history backend. |
| * This will modify the fake history data in the test instance, so that |
| * further 'queryHistory' messages will not contain the deleted entries. |
| * @param {Array} arguments The original arguments to removeVisits. |
| */ |
| removeVisitsStub_: function(args) { |
| for (var i = 0; i < args.length; ++i) { |
| var url = args[i].url; |
| var timestamps = args[i].timestamps; |
| assertEquals(timestamps.length, 1); |
| this.removeVisitsToUrl_(url, new Date(timestamps[0])); |
| } |
| callFrontendAsync('deleteComplete'); |
| }, |
| |
| /** |
| * Removes any visits to |url| on the same day as |date| from the fake |
| * history data. |
| * @param {string} url |
| * @param {Date} date |
| */ |
| removeVisitsToUrl_: function(url, date) { |
| var day = date.toDateString(); |
| var newHistory = []; |
| for (var i = 0, visit; visit = this.fakeHistory_[i]; ++i) { |
| if (url != visit.url || visit.dateRelativeDay != day) |
| newHistory.push(visit); |
| } |
| this.fakeHistory_ = newHistory; |
| } |
| }; |
| |
| /** |
| * Examines the time column of every entry on the page, and ensure that they |
| * are all the same width. |
| */ |
| function ensureTimeWidthsEqual() { |
| var times = document.querySelectorAll('.entry .time'); |
| var timeWidth = times[0].clientWidth; |
| for (var i = 1; i < times.length; ++i) { |
| assertEquals(timeWidth, times[i].clientWidth); |
| } |
| } |
| |
| // Times out on Mac: http://crbug.com/336845 |
| TEST_F('HistoryWebUIFakeBackendTest', 'DISABLED_emptyHistory', function() { |
| expectTrue($('newest-button').hidden); |
| expectTrue($('newer-button').hidden); |
| expectTrue($('older-button').hidden); |
| testDone(); |
| }); |
| |
| // Times out on Win: http://crbug.com/336845 |
| TEST_F('HistoryWebUITest', 'DISABLED_basicTest', function() { |
| var resultCount = document.querySelectorAll('.entry').length; |
| |
| // Check that there are two days of entries. |
| var dayHeaders = document.querySelectorAll('.day'); |
| assertEquals(2, dayHeaders.length); |
| expectNotEquals(dayHeaders[0].textContent, dayHeaders[1].textContent); |
| |
| // Check that the entries in each day are time-ordered, and that no |
| // duplicate URLs appear on a given day. |
| var urlsByDay = {}; |
| var lastDate = new Date(); |
| for (var day = 0; day < dayHeaders.length; ++day) { |
| var dayTitle = dayHeaders[day].textContent; |
| var dayResults = document.querySelectorAll('.day-results')[day]; |
| var entries = dayResults.querySelectorAll('.entry'); |
| expectGT(entries.length, 0); |
| |
| for (var i = 0, entry; entry = entries[i]; ++i) { |
| var time = entry.querySelector('.time').textContent; |
| expectGT(time.length, 0); |
| |
| var date = new Date(dayTitle + ' ' + time); |
| expectGT(lastDate, date); |
| lastDate = date; |
| |
| // Ensure it's not a duplicate URL for this day. |
| var dayAndUrl = day + entry.querySelector('a').href; |
| expectFalse(urlsByDay.hasOwnProperty(dayAndUrl)); |
| urlsByDay[dayAndUrl] = dayAndUrl; |
| |
| // Reconstruct the entry date from the title, and ensure that it's |
| // consistent with the date header and with the time. |
| var entryDate = new Date(entry.querySelector('.title').textContent); |
| expectEquals(entryDate.getYear(), date.getYear()); |
| expectEquals(entryDate.getMonth(), date.getMonth()); |
| expectEquals(entryDate.getDay(), date.getDay()); |
| expectEquals(entryDate.getHours(), date.getHours()); |
| expectEquals(entryDate.getMinutes(), date.getMinutes()); |
| } |
| } |
| |
| // Check that there are 3 page navigation links and that only the "Older" |
| // link is visible. |
| expectEquals(3, document.querySelectorAll('[is="action-link"]').length); |
| expectTrue($('newest-button').hidden); |
| expectTrue($('newer-button').hidden); |
| expectFalse($('older-button').hidden); |
| |
| ensureTimeWidthsEqual(); |
| |
| // Go to the next page. |
| $('older-button').click(); |
| waitForCallback('historyResult', function() { |
| resultCount += document.querySelectorAll('.entry').length; |
| |
| // Check that the two pages include all of the entries. |
| expectEquals(TOTAL_RESULT_COUNT, resultCount); |
| |
| // Check that the day header was properly continued -- the header for the |
| // last day on the first page should be a substring of the header on the |
| // second page. E.g. "Wed, Oct 8, 2008" and "Web, Oct 8, 2008 - cont'd". |
| var newDayHeaders = document.querySelectorAll('.day'); |
| expectEquals(1, newDayHeaders.length); |
| expectEquals(0, |
| newDayHeaders[0].textContent.indexOf(dayHeaders[1].textContent)); |
| |
| // Check that the "Newest" and "Newer" links are now visible, but the |
| // "Older" link is hidden. |
| expectEquals(3, document.querySelectorAll('[is="action-link"]').length); |
| expectFalse($('newest-button').hidden); |
| expectFalse($('newer-button').hidden); |
| expectTrue($('older-button').hidden); |
| |
| ensureTimeWidthsEqual(); |
| |
| // Go back to the first page, and check that the same day headers are there. |
| $('newest-button').click(); |
| var newDayHeaders = document.querySelectorAll('.day'); |
| expectEquals(2, newDayHeaders.length); |
| |
| expectNotEquals(newDayHeaders[0].textContent, |
| newDayHeaders[1].textContent); |
| expectEquals(dayHeaders[0].textContent, newDayHeaders[0].textContent); |
| expectEquals(dayHeaders[1].textContent, newDayHeaders[1].textContent); |
| |
| testDone(); |
| }); |
| }); |
| |
| /** |
| * Test bulk deletion of history entries. |
| * Disabled because it is currently very flaky on the Windows XP bot. |
| */ |
| TEST_F('HistoryWebUITest', 'DISABLED_bulkDeletion', function() { |
| var checkboxes = document.querySelectorAll( |
| '#results-display input[type=checkbox]'); |
| |
| // Immediately confirm the history deletion. |
| confirmDeletion = function(okCallback, cancelCallback) { |
| okCallback(); |
| }; |
| |
| // The "remove" button should be initially disabled. |
| var removeButton = $('remove-selected'); |
| expectTrue(removeButton.disabled); |
| |
| checkboxes[0].click(); |
| expectFalse(removeButton.disabled); |
| |
| var firstEntry = document.querySelector('.title a').textContent; |
| removeButton.click(); |
| |
| // After deletion, expect the results to be reloaded. |
| waitForCallback('historyResult', function() { |
| expectNotEquals(document.querySelector('.title a').textContent, firstEntry); |
| expectTrue(removeButton.disabled); |
| |
| // Delete the first 3 entries. |
| checkboxes = document.querySelectorAll( |
| '#results-display input[type=checkbox]'); |
| checkboxes[0].click(); |
| checkboxes[1].click(); |
| checkboxes[2].click(); |
| expectFalse(removeButton.disabled); |
| |
| var nextEntry = document.querySelectorAll('.title a')[3]; |
| removeButton.click(); |
| waitForCallback('historyResult', function() { |
| // The next entry after the deleted ones should now be the first. |
| expectEquals(document.querySelector('.title a').textContent, |
| nextEntry.textContent); |
| testDone(); |
| }); |
| }); |
| }); |
| |
| /** |
| * Test selecting multiple entries using shift click. |
| * Disabled due to time out on all platforms: crbug/375910 |
| */ |
| TEST_F('HistoryWebUITest', 'DISABLED_multipleSelect', function() { |
| var checkboxes = document.querySelectorAll( |
| '#results-display input[type=checkbox]'); |
| |
| var getAllChecked = function() { |
| return Array.prototype.slice.call(document.querySelectorAll( |
| '#results-display input[type=checkbox]:checked')); |
| }; |
| |
| // Make sure that nothing is checked. |
| expectEquals(0, getAllChecked().length); |
| |
| var shiftClick = function(el) { |
| el.dispatchEvent(new MouseEvent('click', { shiftKey: true })); |
| }; |
| |
| // Check the start. |
| shiftClick($('checkbox-4')); |
| // And the end. |
| shiftClick($('checkbox-9')); |
| |
| // See if they are checked. |
| var checked = getAllChecked(); |
| expectEquals(6, checked.length); |
| checkInterval(checked, 4, 9); |
| |
| // Extend the selection. |
| shiftClick($('checkbox-14')); |
| |
| checked = getAllChecked(); |
| expectEquals(11, checked.length); |
| checkInterval(checked, 4, 14); |
| |
| // Now do a normal click on a higher ID box and a shift click on a lower ID |
| // one (test the other way around). |
| $('checkbox-24').click(); |
| shiftClick($('checkbox-19')); |
| |
| checked = getAllChecked(); |
| expectEquals(17, checked.length); |
| // First set of checkboxes (11). |
| checkInterval(checked, 4, 14); |
| // Second set (6). |
| checkInterval(checked.slice(11), 19, 24); |
| |
| // Test deselection. |
| $('checkbox-26').click(); |
| shiftClick($('checkbox-20')); |
| |
| checked = getAllChecked(); |
| // checkbox-20 to checkbox-24 should be deselected now. |
| expectEquals(12, checked.length); |
| // First set of checkboxes (11). |
| checkInterval(checked, 4, 14); |
| // Only checkbox-19 should still be selected. |
| expectEquals('checkbox-19', checked[11].id); |
| |
| testDone(); |
| }); |
| |
| TEST_F('HistoryWebUITest', 'DISABLED_searchHistory', function() { |
| var getResultCount = function() { |
| return document.querySelectorAll('.entry').length; |
| }; |
| // See that all the elements are there. |
| expectEquals(RESULTS_PER_PAGE, getResultCount()); |
| |
| // See that the search works. |
| $('search-field').value = 'Thu Oct 02 2008'; |
| $('search-button').click(); |
| |
| waitForCallback('historyResult', function() { |
| expectEquals(31, getResultCount()); |
| |
| // Clear the search. |
| $('search-field').value = ''; |
| $('search-button').click(); |
| waitForCallback('historyResult', function() { |
| expectEquals(RESULTS_PER_PAGE, getResultCount()); |
| testDone(); |
| }); |
| }); |
| }); |
| |
| function setPageState(searchText, page, groupByDomain, range, offset) { |
| window.location = '#' + PageState.getHashString( |
| searchText, page, groupByDomain, range, offset); |
| } |
| |
| function RangeHistoryWebUITest() {} |
| |
| RangeHistoryWebUITest.prototype = { |
| __proto__: HistoryWebUITest.prototype, |
| |
| /** @override */ |
| preLoad: function() { |
| HistoryWebUITest.prototype.preLoad.call(this); |
| // Repeat the domain visits every 4 days. The nested lists contain the |
| // domain suffixes for the visits in a day. |
| var domainSuffixByDay = [ |
| [1, 2, 3, 4], |
| [1, 2, 2, 3], |
| [1, 2, 1, 2], |
| [1, 1, 1, 1] |
| ]; |
| |
| var buildDomainUrl = function(timestamp) { |
| var d = new Date(timestamp); |
| // Repeat the same setup of domains every 4 days. |
| var day = d.getDate() % 4; |
| // Assign an entry for every 6 hours so that we get 4 entries per day |
| // maximum. |
| var visitInDay = Math.floor(d.getHours() / 6); |
| return 'http://google' + domainSuffixByDay[day][visitInDay] + '.com/' + |
| timestamp; |
| }; |
| |
| // Prepare a list of fake history results. Start the results on |
| // 11:00 PM on May 2, 2012 and add 4 results every day (one result every 6 |
| // hours). |
| var timestamp = new Date(2012, 4, 2, 23, 0).getTime(); |
| this.today = new Date(2012, 4, 2); |
| this.fakeHistory_ = []; |
| |
| // Put in 2 days for May and 30 days for April so the results span over |
| // the month limit. |
| for (var i = 0; i < 4 * 32; i++) { |
| this.fakeHistory_.push( |
| createHistoryEntry(timestamp, buildDomainUrl(timestamp))); |
| timestamp -= 6 * 60 * 60 * 1000; |
| } |
| |
| // Leave March empty. |
| timestamp -= 31 * 24 * 3600 * 1000; |
| |
| // Put results in February. |
| for (var i = 0; i < 29 * 4; i++) { |
| this.fakeHistory_.push( |
| createHistoryEntry(timestamp, buildDomainUrl(timestamp))); |
| timestamp -= 6 * 60 * 60 * 1000; |
| } |
| }, |
| |
| setUp: function() { |
| // Show the filter controls as if the command line switch was active. |
| $('top-container').hidden = true; |
| $('history-page').classList.add('big-topbar-page'); |
| $('filter-controls').hidden = false; |
| expectFalse($('filter-controls').hidden); |
| }, |
| }; |
| |
| /** |
| * Disabled due intermitent failures on multiple OSes http://crbug.com/377338 |
| */ |
| TEST_F('RangeHistoryWebUITest', 'DISABLED_allView', function() { |
| // Check that we start off in the all time view. |
| expectTrue($('timeframe-controls').querySelector('input').checked); |
| // See if the correct number of days is shown. |
| var dayHeaders = document.querySelectorAll('.day'); |
| assertEquals(Math.ceil(RESULTS_PER_PAGE / 4), dayHeaders.length); |
| testDone(); |
| }); |
| |
| /** |
| * Checks whether the domains in a day are ordered decreasingly. |
| * @param {Element} element Ordered list containing the grouped domains for a |
| * day. |
| */ |
| function checkGroupedVisits(element) { |
| // The history page contains the number of visits next to a domain in |
| // parentheses (e.g. 'google.com (5)'). This function extracts that number |
| // and returns it. |
| var getNumberVisits = function(element) { |
| return parseInt(element.textContent.replace(/\D/g, ''), 10); |
| }; |
| |
| // Read the number of visits from each domain and make sure that it is lower |
| // than or equal to the number of visits from the previous domain. |
| var domainEntries = element.querySelectorAll('.number-visits'); |
| var currentNumberOfVisits = getNumberVisits(domainEntries[0]); |
| for (var j = 1; j < domainEntries.length; j++) { |
| var numberOfVisits = getNumberVisits(domainEntries[j]); |
| assertTrue(currentNumberOfVisits >= numberOfVisits); |
| currentNumberOfVisits = numberOfVisits; |
| } |
| } |
| |
| // Times out on Mac and Win: http://crbug.com/336845 |
| TEST_F('RangeHistoryWebUITest', 'DISABLED_weekViewGrouped', function() { |
| // Change to weekly view. |
| setPageState('', 0, HistoryModel.Range.WEEK, 0); |
| waitForCallback('historyResult', function() { |
| // See if the correct number of days is still shown. |
| var dayResults = document.querySelectorAll('.day-results'); |
| assertEquals(7, dayResults.length); |
| |
| // Check whether the results are ordered by visits. |
| for (var i = 0; i < dayResults.length; i++) |
| checkGroupedVisits(dayResults[i]); |
| |
| ensureTimeWidthsEqual(); |
| |
| testDone(); |
| }); |
| }); |
| |
| // Times out on Mac and Win: http://crbug.com/336845 |
| TEST_F('RangeHistoryWebUITest', 'DISABLED_monthViewGrouped', function() { |
| // Change to monthly view. |
| setPageState('', 0, HistoryModel.Range.MONTH, 0); |
| waitForCallback('historyResult', function() { |
| // See if the correct number of days is shown. |
| var monthResults = document.querySelectorAll('.month-results'); |
| assertEquals(1, monthResults.length); |
| |
| checkGroupedVisits(monthResults[0]); |
| ensureTimeWidthsEqual(); |
| |
| testDone(); |
| }); |
| }); |
| |
| // Times out on Mac: http://crbug.com/336845 |
| TEST_F('RangeHistoryWebUITest', 'DISABLED_monthViewEmptyMonth', function() { |
| // Change to monthly view. |
| setPageState('', 0, HistoryModel.Range.MONTH, 2); |
| |
| waitForCallback('historyResult', function() { |
| // See if the correct number of days is shown. |
| var resultsDisplay = $('results-display'); |
| assertEquals(0, resultsDisplay.querySelectorAll('.months-results').length); |
| var noResults = loadTimeData.getString('noResults'); |
| assertNotEquals(-1, $('results-header').textContent.indexOf(noResults)); |
| |
| testDone(); |
| }); |
| }); |
| |
| /** |
| * Fixture for History WebUI tests using the real history backend. |
| * @extends {BaseHistoryWebUITest} |
| * @constructor |
| */ |
| function HistoryWebUIRealBackendTest() {} |
| |
| HistoryWebUIRealBackendTest.prototype = { |
| __proto__: BaseHistoryWebUITest.prototype, |
| |
| /** @override */ |
| testGenPreamble: function() { |
| // Add some visits to the history database. |
| GEN(' AddPageToHistory(0, "http://google.com", "Google");'); |
| GEN(' AddPageToHistory(1, "http://example.com", "Example");'); |
| GEN(' AddPageToHistory(2, "http://google.com", "Google");'); |
| |
| // Add a visit on the next day. |
| GEN(' AddPageToHistory(36, "http://google.com", "Google");'); |
| }, |
| |
| /** @override */ |
| setUp: function() { |
| BaseHistoryWebUITest.prototype.setUp.call(this); |
| |
| // Enable when failure is resolved. |
| // AX_TEXT_04: http://crbug.com/560914 |
| this.accessibilityAuditConfig.ignoreSelectors( |
| 'linkWithUnclearPurpose', |
| '#notification-bar > SPAN > A'); |
| }, |
| }; |
| |
| /** |
| * Simple test that verifies that the correct entries are retrieved from the |
| * history database and displayed in the UI. |
| */ |
| // Times out on Mac and Win: http://crbug.com/336845 |
| TEST_F('HistoryWebUIRealBackendTest', 'DISABLED_basic', function() { |
| // Check that there are two days of entries, and three entries in total. |
| assertEquals(2, document.querySelectorAll('.day').length); |
| assertEquals(3, document.querySelectorAll('.entry').length); |
| |
| testDone(); |
| }); |
| |
| // Times out on Mac: http://crbug.com/336845 |
| TEST_F('HistoryWebUIRealBackendTest', |
| 'DISABLED_atLeastOneFocusable', function() { |
| var results = document.querySelectorAll('#results-display [tabindex="0"]'); |
| expectGE(results.length, 1); |
| testDone(); |
| }); |
| |
| // Times out on Mac: http://crbug.com/336845 |
| TEST_F('HistoryWebUIRealBackendTest', |
| 'DISABLED_deleteRemovesEntry', function() { |
| assertTrue(historyModel.deletingHistoryAllowed); |
| |
| var visit = document.querySelector('.entry').visit; |
| visit.titleLink.focus(); |
| assertEquals(visit.titleLink, document.activeElement); |
| |
| var deleteKey = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'Delete'}); |
| |
| assertFalse(historyModel.isDeletingVisits()); |
| expectFalse(visit.titleLink.dispatchEvent(deleteKey)); |
| expectTrue(historyModel.isDeletingVisits()); |
| |
| expectNotEquals(visit.dropDown, document.activeElement); |
| testDone(); |
| }); |
| |
| /** |
| * Test individual deletion of history entries. |
| */ |
| TEST_F('HistoryWebUIRealBackendTest', 'singleDeletion', function() { |
| // Deletes the history entry represented by |entryElement|, and calls callback |
| // when the deletion is complete. |
| var removeEntry = function(entryElement, callback) { |
| var dropDownButton = entryElement.querySelector('.drop-down'); |
| var removeMenuItem = $('remove-visit'); |
| |
| assertFalse(dropDownButton.disabled); |
| assertFalse(removeMenuItem.disabled); |
| |
| waitForCallback('onEntryRemoved', callback); |
| |
| cr.dispatchSimpleEvent(dropDownButton, 'mousedown'); |
| |
| var e = new Event('command', {bubbles: true}); |
| e.command = removeMenuItem.command; |
| removeMenuItem.dispatchEvent(e); |
| }; |
| |
| var secondTitle = document.querySelectorAll('.entry a')[1].textContent; |
| var thirdTitle = document.querySelectorAll('.entry a')[2].textContent; |
| |
| // historyDeleted() should not be called when deleting individual entries |
| // using the drop down. |
| waitForCallback('historyDeleted', function() { |
| testDone([false, 'historyDeleted() called when deleting single entry']); |
| }); |
| |
| expectEquals(2, document.querySelectorAll('.day').length); |
| |
| // Delete the first entry. The previous second entry should now be the first. |
| removeEntry(document.querySelector('.entry'), function() { |
| expectEquals(secondTitle, document.querySelector('.entry a').textContent); |
| |
| // After removing the first entry, its day header should also be gone. |
| expectEquals(1, document.querySelectorAll('.day').length); |
| |
| // Delete another entry. The original third entry should now be the first. |
| removeEntry(document.querySelector('.entry'), function() { |
| expectEquals(thirdTitle, document.querySelector('.entry a').textContent); |
| testDone(); |
| }); |
| }); |
| }); |
| |
| TEST_F('HistoryWebUIRealBackendTest', 'leftRightChangeFocus', function() { |
| var visit = document.querySelector('.entry').visit; |
| visit.titleLink.focus(); |
| assertEquals(visit.titleLink, document.activeElement); |
| |
| var right = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'ArrowRight'}); |
| expectFalse(visit.titleLink.dispatchEvent(right)); |
| |
| assertEquals(visit.dropDown, document.activeElement); |
| |
| var left = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'ArrowLeft'}); |
| expectFalse(visit.dropDown.dispatchEvent(left)); |
| |
| expectEquals(visit.titleLink, document.activeElement); |
| testDone(); |
| }); |
| |
| TEST_F('HistoryWebUIRealBackendTest', 'showConfirmDialogAndCancel', function() { |
| waitForCallback('deleteComplete', function() { |
| testDone([false, "history deleted when it shouldn't have been"]); |
| }); |
| |
| document.querySelector('input[type=checkbox]').click(); |
| $('remove-selected').click(); |
| |
| assertTrue($('alertOverlay').classList.contains('showing')); |
| assertFalse($('history-page').contains(document.activeElement)); |
| |
| var esc = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'Escape'}); |
| |
| document.documentElement.dispatchEvent(esc); |
| assertFalse($('alertOverlay').classList.contains('showing')); |
| |
| testDone(); |
| }); |
| |
| TEST_F('HistoryWebUIRealBackendTest', 'showConfirmDialogAndRemove', function() { |
| document.querySelector('input[type=checkbox]').click(); |
| $('remove-selected').click(); |
| |
| assertTrue($('alertOverlay').classList.contains('showing')); |
| assertFalse($('history-page').contains(document.activeElement)); |
| |
| waitForCallback('deleteComplete', testDone); |
| |
| var enter = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'Enter'}); |
| document.documentElement.dispatchEvent(enter); |
| assertFalse($('alertOverlay').classList.contains('showing')); |
| }); |
| |
| // Times out on Mac: http://crbug.com/336845 |
| TEST_F('HistoryWebUIRealBackendTest', |
| 'DISABLED_menuButtonActivatesOneRow', function() { |
| var entries = document.querySelectorAll('.entry'); |
| assertEquals(3, entries.length); |
| assertTrue(entries[0].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| assertTrue($('action-menu').hidden); |
| |
| // Show the menu via mousedown on the menu button. |
| var menuButton = entries[2].querySelector('.menu-button'); |
| menuButton.dispatchEvent(new MouseEvent('mousedown')); |
| expectFalse($('action-menu').hidden); |
| |
| // Check that the active item has changed. |
| expectTrue(entries[2].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| expectFalse(entries[0].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| |
| testDone(); |
| }); |
| |
| // Flaky: http://crbug.com/527434 |
| TEST_F('HistoryWebUIRealBackendTest', |
| 'DISABLED_shiftClickActivatesOneRow', function () { |
| var entries = document.querySelectorAll('.entry'); |
| assertEquals(3, entries.length); |
| assertTrue(entries[0].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| |
| entries[0].visit.checkBox.focus(); |
| assertEquals(entries[0].visit.checkBox, document.activeElement); |
| |
| entries[0].visit.checkBox.click(); |
| assertTrue(entries[0].visit.checkBox.checked); |
| |
| var entryBox = entries[2].querySelector('.entry-box'); |
| entryBox.dispatchEvent(new MouseEvent('click', {shiftKey: true})); |
| assertTrue(entries[1].visit.checkBox.checked); |
| |
| // Focus shouldn't have changed, but the checkbox should toggle. |
| expectEquals(entries[0].visit.checkBox, document.activeElement); |
| |
| expectTrue(entries[0].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| expectFalse(entries[2].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| |
| var shiftDown = new MouseEvent('mousedown', {shiftKey: true, bubbles: true}); |
| entries[2].visit.checkBox.dispatchEvent(shiftDown); |
| expectEquals(entries[2].visit.checkBox, document.activeElement); |
| |
| // 'focusin' events aren't dispatched while tests are run in batch (e.g. |
| // --test-launcher-jobs=2). Simulate this. TODO(dbeam): fix instead. |
| cr.dispatchSimpleEvent(document.activeElement, 'focusin', true, true); |
| |
| expectFalse(entries[0].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| expectTrue(entries[2].classList.contains(cr.ui.FocusRow.ACTIVE_CLASS)); |
| |
| testDone(); |
| }); |
| |
| /** |
| * Fixture for History WebUI testing when deletions are prohibited. |
| * @extends {HistoryWebUIRealBackendTest} |
| * @constructor |
| */ |
| function HistoryWebUIDeleteProhibitedTest() {} |
| |
| HistoryWebUIDeleteProhibitedTest.prototype = { |
| __proto__: HistoryWebUIRealBackendTest.prototype, |
| |
| /** @override */ |
| testGenPreamble: function() { |
| HistoryWebUIRealBackendTest.prototype.testGenPreamble.call(this); |
| GEN(' SetDeleteAllowed(false);'); |
| }, |
| }; |
| |
| // Test UI when removing entries is prohibited. |
| // Times out on Mac: http://crbug.com/336845 |
| TEST_F('HistoryWebUIDeleteProhibitedTest', |
| 'DISABLED_deleteProhibited', function() { |
| // No checkboxes should be created. |
| var checkboxes = document.querySelectorAll( |
| '#results-display input[type=checkbox]'); |
| expectEquals(0, checkboxes.length); |
| |
| // The "remove" button should be disabled. |
| var removeButton = $('remove-selected'); |
| expectTrue(removeButton.disabled); |
| |
| // The "Remove from history" drop-down item should be disabled. |
| var removeVisit = $('remove-visit'); |
| expectTrue(removeVisit.disabled); |
| |
| testDone(); |
| }); |
| |
| TEST_F('HistoryWebUIDeleteProhibitedTest', 'atLeastOneFocusable', function() { |
| var results = document.querySelectorAll('#results-display [tabindex="0"]'); |
| expectGE(results.length, 1); |
| testDone(); |
| }); |
| |
| TEST_F('HistoryWebUIDeleteProhibitedTest', 'leftRightChangeFocus', function() { |
| var visit = document.querySelector('.entry').visit; |
| visit.titleLink.focus(); |
| assertEquals(visit.titleLink, document.activeElement); |
| |
| var right = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'ArrowRight'}); |
| expectFalse(visit.titleLink.dispatchEvent(right)); |
| |
| assertEquals(visit.dropDown, document.activeElement); |
| |
| var left = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'ArrowLeft'}); |
| expectFalse(visit.dropDown.dispatchEvent(left)); |
| |
| expectEquals(visit.titleLink, document.activeElement); |
| testDone(); |
| }); |
| |
| TEST_F('HistoryWebUIDeleteProhibitedTest', 'deleteIgnored', function() { |
| assertFalse(historyModel.deletingHistoryAllowed); |
| |
| var visit = document.querySelector('.entry').visit; |
| visit.titleLink.focus(); |
| assertEquals(visit.titleLink, document.activeElement); |
| |
| var deleteKey = new KeyboardEvent('keydown', |
| {bubbles: true, cancelable: true, key: 'Delete'}); |
| |
| assertFalse(historyModel.isDeletingVisits()); |
| expectTrue(visit.titleLink.dispatchEvent(deleteKey)); |
| expectFalse(historyModel.isDeletingVisits()); |
| |
| expectEquals(visit.titleLink, document.activeElement); |
| testDone(); |
| }); |
| |
| /** |
| * Fixture for History WebUI testing IDN. |
| * @extends {BaseHistoryWebUITest} |
| * @constructor |
| */ |
| function HistoryWebUIIDNTest() {} |
| |
| HistoryWebUIIDNTest.prototype = { |
| __proto__: BaseHistoryWebUITest.prototype, |
| |
| /** @override */ |
| testGenPreamble: function() { |
| // Add some visits to the history database. |
| GEN(' AddPageToHistory(0, "http://xn--d1abbgf6aiiy.xn--p1ai/",' + |
| ' "Some");'); |
| |
| // Clear AcceptLanguages to get domain in unicode. |
| GEN(' ClearAcceptLanguages();'); |
| }, |
| }; |
| |
| /** |
| * Simple test that verifies that the correct entries are retrieved from the |
| * history database and displayed in the UI. |
| */ |
| // Times out on Mac: http://crbug.com/336845 |
| TEST_F('HistoryWebUIIDNTest', 'DISABLED_basic', function() { |
| // Check that there is only one entry and domain is in unicode. |
| assertEquals(1, document.querySelectorAll('.domain').length); |
| assertEquals("\u043f\u0440\u0435\u0437\u0438\u0434\u0435\u043d\u0442." + |
| "\u0440\u0444", document.querySelector('.domain').textContent); |
| |
| testDone(); |
| }); |
| |
| /** |
| * Fixture for a test that uses the real backend and tests how the history |
| * page deals with odd schemes in URLs. |
| * @extends {HistoryWebUIRealBackendTest} |
| */ |
| function HistoryWebUIWithSchemesTest() {} |
| |
| HistoryWebUIWithSchemesTest.prototype = { |
| __proto__: HistoryWebUIRealBackendTest.prototype, |
| |
| /** @override */ |
| testGenPreamble: function() { |
| // Add a bunch of entries on the same day, including some weird schemes. |
| GEN(' AddPageToHistory(12, "http://google.com", "Google");'); |
| GEN(' AddPageToHistory(13, "file:///tmp/foo", "");'); |
| GEN(' AddPageToHistory(14, "mailto:chromium@chromium.org", "");'); |
| GEN(' AddPageToHistory(15, "tel:555123456", "");'); |
| }, |
| |
| setUp: function() { |
| // Show the filter controls as if the command line switch was active. |
| $('top-container').hidden = true; |
| $('history-page').classList.add('big-topbar-page'); |
| $('filter-controls').hidden = false; |
| expectFalse($('filter-controls').hidden); |
| }, |
| }; |
| |
| TEST_F('HistoryWebUIWithSchemesTest', 'groupingWithSchemes', function() { |
| // Switch to the week view. |
| $('timeframe-controls').querySelectorAll('input')[1].click(); |
| waitForCallback('historyResult', function() { |
| // Each URL should be organized under a different "domain". |
| expectEquals(document.querySelectorAll('.entry').length, 4); |
| expectEquals(document.querySelectorAll('.site-domain-wrapper').length, 4); |
| testDone(); |
| }); |
| }); |