Commit d58b9ebb authored by Roman Arora's avatar Roman Arora Committed by Josip Sokcevic

Tab Search: Scrolling test

Bug: 1099917
Change-Id: I8430ce511cc625ec06bf87402e643a4a540386af
Reviewed-on: https://chrome-internal-review.googlesource.com/c/chrome/browser/resources/tab_search/+/3284870Reviewed-by: default avatarYuheng Huang <yuhengh@chromium.org>
Reviewed-by: default avatarTom Lukaszewicz <tluk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#819622}
parent 02e8ad6a
...@@ -7,12 +7,14 @@ import {getDeepActiveElement} from 'chrome://resources/js/util.m.js'; ...@@ -7,12 +7,14 @@ import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js'; import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
import {TabSearchAppElement} from 'chrome://tab-search/app.js'; import {TabSearchAppElement} from 'chrome://tab-search/app.js';
import {TabSearchApiProxy, TabSearchApiProxyImpl} from 'chrome://tab-search/tab_search_api_proxy.js' import {TabSearchApiProxy, TabSearchApiProxyImpl} from 'chrome://tab-search/tab_search_api_proxy.js'
import {TabSearchItem} from 'chrome://tab-search/tab_search_item.js';
import {TabSearchSearchField} from 'chrome://tab-search/tab_search_search_field.js'; import {TabSearchSearchField} from 'chrome://tab-search/tab_search_search_field.js';
import {assertEquals} from '../../chai_assert.js'; import {assertEquals, assertGT} from '../../chai_assert.js';
import {flushTasks} from '../../test_util.m.js'; import {flushTasks} from '../../test_util.m.js';
import {sampleData} from './tab_search_test_data.js'; import {generateSampleDataFromSiteNames, sampleData, sampleSiteNames} from './tab_search_test_data.js';
import {assertTabItemAndNeighborsInViewBounds, disableScrollIntoViewAnimations} from './tab_search_test_helper.js';
import {TestTabSearchApiProxy} from './test_tab_search_api_proxy.js'; import {TestTabSearchApiProxy} from './test_tab_search_api_proxy.js';
suite('TabSearchAppFocusTest', () => { suite('TabSearchAppFocusTest', () => {
...@@ -21,21 +23,32 @@ suite('TabSearchAppFocusTest', () => { ...@@ -21,21 +23,32 @@ suite('TabSearchAppFocusTest', () => {
/** @type {!TestTabSearchApiProxy} */ /** @type {!TestTabSearchApiProxy} */
let testProxy; let testProxy;
setup(async () => { disableScrollIntoViewAnimations(TabSearchItem);
/**
* @param {tabSearch.mojom.ProfileTabs} sampleData
* @param {Object=} loadTimeOverridenData
*/
async function setupTest(sampleData, loadTimeOverridenData) {
testProxy = new TestTabSearchApiProxy(); testProxy = new TestTabSearchApiProxy();
testProxy.setProfileTabs(sampleData()); testProxy.setProfileTabs(sampleData);
TabSearchApiProxyImpl.instance_ = testProxy; TabSearchApiProxyImpl.instance_ = testProxy;
loadTimeData.overrideValues({'submitFeedbackEnabled': true}); if (loadTimeOverridenData) {
loadTimeData.overrideValues(loadTimeOverridenData);
}
tabSearchApp = /** @type {!TabSearchAppElement} */ tabSearchApp = /** @type {!TabSearchAppElement} */
(document.createElement('tab-search-app')); (document.createElement('tab-search-app'));
document.body.innerHTML = ''; document.body.innerHTML = '';
document.body.appendChild(tabSearchApp); document.body.appendChild(tabSearchApp);
await flushTasks(); await flushTasks();
}); }
test('KeyNavigation', async () => { test('KeyNavigation', async () => {
await setupTest(sampleData(), {'submitFeedbackEnabled': true});
// Initially, the search input should have focus. // Initially, the search input should have focus.
const searchField = /** @type {!TabSearchSearchField} */ const searchField = /** @type {!TabSearchSearchField} */
(tabSearchApp.shadowRoot.querySelector('#searchField')); (tabSearchApp.shadowRoot.querySelector('#searchField'));
...@@ -74,6 +87,8 @@ suite('TabSearchAppFocusTest', () => { ...@@ -74,6 +87,8 @@ suite('TabSearchAppFocusTest', () => {
}); });
test('KeyPress', async () => { test('KeyPress', async () => {
await setupTest(sampleData());
const tabSearchItem = /** @type {!HTMLElement} */ const tabSearchItem = /** @type {!HTMLElement} */
(tabSearchApp.shadowRoot.querySelector('tab-search-item')); (tabSearchApp.shadowRoot.querySelector('tab-search-item'));
tabSearchItem.focus(); tabSearchItem.focus();
...@@ -87,4 +102,22 @@ suite('TabSearchAppFocusTest', () => { ...@@ -87,4 +102,22 @@ suite('TabSearchAppFocusTest', () => {
keyDownOn(closeButton, 0, [], 'Enter'); keyDownOn(closeButton, 0, [], 'Enter');
assertEquals(1, testProxy.getCallCount('closeTab')); assertEquals(1, testProxy.getCallCount('closeTab'));
}); });
test('ViewScrolling', async () => {
await setupTest(generateSampleDataFromSiteNames(sampleSiteNames()));
const tabsDiv = /** @type {!HTMLElement} */
(tabSearchApp.shadowRoot.querySelector('#tabs'));
// Assert that the tabs are in a overflowing state.
assertGT(tabsDiv.scrollHeight, tabsDiv.clientHeight);
const tabItems = /** @type {!NodeList<HTMLElement>} */
(tabSearchApp.shadowRoot.querySelectorAll('tab-search-item'));
for (let i = 0; i < tabItems.length; i++) {
tabItems[i].focus();
assertEquals(i, tabSearchApp.getSelectedIndex());
assertTabItemAndNeighborsInViewBounds(tabsDiv, tabItems, i);
}
});
}); });
...@@ -6,12 +6,14 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; ...@@ -6,12 +6,14 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js'; import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
import {TabSearchAppElement} from 'chrome://tab-search/app.js'; import {TabSearchAppElement} from 'chrome://tab-search/app.js';
import {TabSearchApiProxy, TabSearchApiProxyImpl} from 'chrome://tab-search/tab_search_api_proxy.js' import {TabSearchApiProxy, TabSearchApiProxyImpl} from 'chrome://tab-search/tab_search_api_proxy.js'
import {TabSearchItem} from 'chrome://tab-search/tab_search_item.js';
import {TabSearchSearchField} from 'chrome://tab-search/tab_search_search_field.js'; import {TabSearchSearchField} from 'chrome://tab-search/tab_search_search_field.js';
import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js'; import {assertEquals, assertFalse, assertGT, assertNotEquals, assertTrue} from '../../chai_assert.js';
import {flushTasks, waitAfterNextRender} from '../../test_util.m.js'; import {flushTasks, waitAfterNextRender} from '../../test_util.m.js';
import {sampleData} from './tab_search_test_data.js'; import {generateSampleDataFromSiteNames, sampleData, sampleSiteNames} from './tab_search_test_data.js';
import {assertTabItemAndNeighborsInViewBounds, assertTabItemInViewBounds, disableScrollIntoViewAnimations} from './tab_search_test_helper.js';
import {TestTabSearchApiProxy} from './test_tab_search_api_proxy.js'; import {TestTabSearchApiProxy} from './test_tab_search_api_proxy.js';
suite('TabSearchAppTest', () => { suite('TabSearchAppTest', () => {
...@@ -20,6 +22,8 @@ suite('TabSearchAppTest', () => { ...@@ -20,6 +22,8 @@ suite('TabSearchAppTest', () => {
/** @type {!TestTabSearchApiProxy} */ /** @type {!TestTabSearchApiProxy} */
let testProxy; let testProxy;
disableScrollIntoViewAnimations(TabSearchItem);
/** /**
* @param {!NodeList<!Element>} rows * @param {!NodeList<!Element>} rows
* @param {!Array<number>} ids * @param {!Array<number>} ids
...@@ -387,4 +391,39 @@ suite('TabSearchAppTest', () => { ...@@ -387,4 +391,39 @@ suite('TabSearchAppTest', () => {
assertEquals(4, testProxy.getCallCount('closeUI')); assertEquals(4, testProxy.getCallCount('closeUI'));
}); });
test('Scrollbar updates show previous and following list items', async () => {
await setupTest(generateSampleDataFromSiteNames(sampleSiteNames()));
const tabsDiv = /** @type {!HTMLElement} */
(tabSearchApp.shadowRoot.querySelector('#tabs'));
// Assert that the tabs are in a overflowing state.
assertGT(tabsDiv.scrollHeight, tabsDiv.clientHeight);
const tabItems = /** @type {!NodeList<!HTMLElement>}*/ (queryRows());
const searchField = /** @type {!TabSearchSearchField} */
(tabSearchApp.shadowRoot.querySelector('#searchField'));
for (let i = 0; i < tabItems.length; i++) {
keyDownOn(searchField, 0, [], 'ArrowDown');
const selectedIndex = ((i + 1) % tabItems.length);
assertEquals(selectedIndex, tabSearchApp.getSelectedIndex());
assertTabItemAndNeighborsInViewBounds(tabsDiv, tabItems, selectedIndex);
}
keyDownOn(searchField, 0, [], 'End');
assertTabItemInViewBounds(tabsDiv, tabItems[tabItems.length - 1]);
for (let i = tabItems.length - 1; i >= 0; i--) {
keyDownOn(searchField, 0, [], 'ArrowUp');
const selectedIndex = (i - 1 + tabItems.length) % tabItems.length;
assertEquals(selectedIndex, tabSearchApp.getSelectedIndex());
assertTabItemAndNeighborsInViewBounds(tabsDiv, tabItems, selectedIndex);
}
keyDownOn(searchField, 0, [], 'Home');
assertTabItemInViewBounds(tabsDiv, tabItems[0]);
});
}); });
...@@ -25,7 +25,7 @@ export function sampleData() { ...@@ -25,7 +25,7 @@ export function sampleData() {
tabId: 6, tabId: 6,
title: 'Apple', title: 'Apple',
url: 'https://www.apple.com', url: 'https://www.apple.com',
} },
], ],
}, },
{ {
...@@ -49,10 +49,42 @@ export function sampleData() { ...@@ -49,10 +49,42 @@ export function sampleData() {
title: 'Apple', title: 'Apple',
url: 'https://www.apple.com/', url: 'https://www.apple.com/',
}, },
] ],
} }
] ]
}; };
return profileTabs; return profileTabs;
} }
export function sampleSiteNames() {
return [
'Google',
'Amazon',
'Apple',
'Bing',
'Yahoo',
'PayPal',
'Square',
'Youtube',
'Facebook',
'Twitter',
];
}
/**
* Generates profile data for a window with a series of tabs.
* @param {!Array} siteNames
*/
export function generateSampleDataFromSiteNames(siteNames) {
const tabs = siteNames.map((siteName, i) => {
return {
index: i,
tabId: i + 1,
title: siteName,
url: 'https://www.' + siteName.toLowerCase() + '.com',
};
});
return {windows: [{active: true, tabs: tabs}]};
}
// Copyright 2020 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.
import {assertGE, assertLE} from '../../chai_assert.js';
/**
* Override the scrollIntoView function and parameters of the given class to
* avoid smooth scroll animations that delay the scrollTop property updates.
* @param {!Object} klass
*/
export function disableScrollIntoViewAnimations(klass) {
const originalScrollFunc = klass.prototype.scrollIntoView;
klass.prototype.scrollIntoView = function(options) {
const args = [];
if (typeof options === 'object' && options !== null) {
let noAnimationOptions = Object.assign({}, options);
delete noAnimationOptions.behavior;
args.push(noAnimationOptions);
}
originalScrollFunc.apply(this, args);
};
}
/**
* Assert that the tabItem HTML element is fully visible within the current
* scroll view.
* @param {!HTMLElement} tabsDiv
* @param {!HTMLElement} tabItem
*/
export function assertTabItemInViewBounds(tabsDiv, tabItem) {
assertGE(tabItem.offsetTop, tabsDiv.scrollTop);
assertLE(
tabItem.offsetTop + tabItem.offsetHeight,
tabsDiv.scrollTop + tabsDiv.offsetHeight);
}
/**
* @param {!HTMLElement} tabsDiv The HTML element containing a list of tab
* items.
* @param {!NodeList<!HTMLElement>} tabItems A list of tab items.
* @param {number} index The tab item's index in the list of tab items.
*/
export function assertTabItemAndNeighborsInViewBounds(
tabsDiv, tabItems, index) {
if (index > 0) {
assertTabItemInViewBounds(tabsDiv, tabItems[index - 1]);
}
assertTabItemInViewBounds(tabsDiv, tabItems[index]);
if (index < tabItems.length - 1) {
assertTabItemInViewBounds(tabsDiv, tabItems[index + 1]);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment