blob: d7468cb61345b831e81b876d8b06b4d850e897cf [file] [log] [blame]
// Copyright 2017 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.
/**
* @fileoverview Tests for MD Bookmarks which are run as interactive ui tests.
* Should be used for tests which care about focus.
*/
const ROOT_PATH = '../../../../../';
GEN_INCLUDE(
[ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']);
GEN('#include "chrome/common/chrome_features.h"');
function MaterialBookmarksFocusTest() {}
MaterialBookmarksFocusTest.prototype = {
__proto__: PolymerInteractiveUITest.prototype,
browsePreload: 'chrome://bookmarks',
featureList: ['features::kMaterialDesignBookmarks', ''],
extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
'test_command_manager.js',
'test_store.js',
'test_util.js',
]),
};
TEST_F('MaterialBookmarksFocusTest', 'All', function() {
suite('<bookmarks-folder-node>', function() {
let rootNode;
let store;
function getFolderNode(id) {
return findFolderNode(rootNode, id);
}
function keydown(id, key) {
MockInteractions.keyDownOn(getFolderNode(id).$.container, '', [], key);
}
setup(function() {
const nodes = testTree(
createFolder(
'1',
[
createFolder(
'2',
[
createFolder('3', []),
createFolder('4', []),
]),
createItem('5'),
]),
createFolder('7', []));
store = new bookmarks.TestStore({
nodes: nodes,
folderOpenState: getAllFoldersOpenState(nodes),
selectedFolder: '1',
});
store.setReducersEnabled(true);
store.replaceSingleton();
rootNode = document.createElement('bookmarks-folder-node');
rootNode.itemId = '0';
rootNode.depth = -1;
replaceBody(rootNode);
Polymer.dom.flush();
});
test('keyboard selection', function() {
function assertFocused(oldFocus, newFocus) {
assertEquals(
'-1', getFolderNode(oldFocus).$.container.getAttribute('tabindex'));
assertEquals(
'0', getFolderNode(newFocus).$.container.getAttribute('tabindex'));
assertEquals(
getFolderNode(newFocus).$.container,
getFolderNode(newFocus).root.activeElement);
}
store.data.folderOpenState.set('2', false);
store.notifyObservers();
// The selected folder is focus enabled on attach.
assertEquals(
'0', getFolderNode('1').$.container.getAttribute('tabindex'));
// Only the selected folder should be keyboard focusable.
assertEquals(
'-1', getFolderNode('2').$.container.getAttribute('tabindex'));
store.data.search.term = 'asdf';
// The selected folder is focus enabled even with a search term.
assertEquals(
'0', getFolderNode('1').$.container.getAttribute('tabindex'));
store.data.search.term = '';
// Give keyboard focus to the first item.
getFolderNode('1').$.container.focus();
// Move down into child.
keydown('1', 'ArrowDown');
assertDeepEquals(bookmarks.actions.selectFolder('2'), store.lastAction);
store.data.selectedFolder = '2';
store.notifyObservers();
assertEquals(
'-1', getFolderNode('1').$.container.getAttribute('tabindex'));
assertEquals(
'0', getFolderNode('2').$.container.getAttribute('tabindex'));
assertFocused('1', '2');
// Move down past closed folders.
keydown('2', 'ArrowDown');
assertDeepEquals(bookmarks.actions.selectFolder('7'), store.lastAction);
assertFocused('2', '7');
// Move down past end of list.
store.resetLastAction();
keydown('7', 'ArrowDown');
assertDeepEquals(null, store.lastAction);
// Move up past closed folders.
keydown('7', 'ArrowUp');
assertDeepEquals(bookmarks.actions.selectFolder('2'), store.lastAction);
assertFocused('7', '2');
// Move up into parent.
keydown('2', 'ArrowUp');
assertDeepEquals(bookmarks.actions.selectFolder('1'), store.lastAction);
assertFocused('2', '1');
// Move up past start of list.
store.resetLastAction();
keydown('1', 'ArrowUp');
assertDeepEquals(null, store.lastAction);
});
test('keyboard left/right', function() {
store.data.folderOpenState.set('2', false);
store.notifyObservers();
// Give keyboard focus to the first item.
getFolderNode('1').$.container.focus();
// Pressing right descends into first child.
keydown('1', 'ArrowRight');
assertDeepEquals(bookmarks.actions.selectFolder('2'), store.lastAction);
// Pressing right on a closed folder opens that folder
keydown('2', 'ArrowRight');
assertDeepEquals(
bookmarks.actions.changeFolderOpen('2', true), store.lastAction);
// Pressing right again descends into first child.
keydown('2', 'ArrowRight');
assertDeepEquals(bookmarks.actions.selectFolder('3'), store.lastAction);
// Pressing right on a folder with no children does nothing.
store.resetLastAction();
keydown('3', 'ArrowRight');
assertDeepEquals(null, store.lastAction);
// Pressing left on a folder with no children ascends to parent.
keydown('3', 'ArrowDown');
keydown('4', 'ArrowLeft');
assertDeepEquals(bookmarks.actions.selectFolder('2'), store.lastAction);
// Pressing left again closes the parent.
keydown('2', 'ArrowLeft');
assertDeepEquals(
bookmarks.actions.changeFolderOpen('2', false), store.lastAction);
// RTL flips left and right.
document.body.style.direction = 'rtl';
keydown('2', 'ArrowLeft');
assertDeepEquals(
bookmarks.actions.changeFolderOpen('2', true), store.lastAction);
keydown('2', 'ArrowRight');
assertDeepEquals(
bookmarks.actions.changeFolderOpen('2', false), store.lastAction);
document.body.style.direction = 'ltr';
});
test('keyboard commands are passed to command manager', function() {
const commandManager = new TestCommandManager();
document.body.appendChild(commandManager);
chrome.bookmarkManagerPrivate.removeTrees = function() {};
store.data.selection.items = new Set(['3', '4']);
store.data.selectedFolder = '2';
store.notifyObservers();
getFolderNode('2').$.container.focus();
keydown('2', 'Delete');
commandManager.assertLastCommand(Command.DELETE, ['2']);
});
});
suite('<bookmarks-list>', function() {
let list;
let store;
let items;
let commandManager;
const multiKey = cr.isMac ? 'meta' : 'ctrl';
function keydown(item, key, modifiers) {
MockInteractions.keyDownOn(item, '', modifiers, key);
}
setup(function() {
const nodes = testTree(createFolder('1', [
createItem('2'),
createItem('3'),
createItem('4'),
createItem('5'),
createItem('6'),
createFolder('7', []),
]));
store = new bookmarks.TestStore({
nodes: nodes,
folderOpenState: getAllFoldersOpenState(nodes),
selectedFolder: '1',
});
store.setReducersEnabled(true);
store.replaceSingleton();
list = document.createElement('bookmarks-list');
list.style.height = '100%';
list.style.width = '100%';
list.style.position = 'absolute';
replaceBody(list);
Polymer.dom.flush();
items = list.root.querySelectorAll('bookmarks-item');
commandManager = new TestCommandManager();
document.body.appendChild(commandManager);
});
test('simple keyboard selection', function() {
let focusedItem = items[0];
assertEquals('0', focusedItem.getAttribute('tabindex'));
assertEquals(
'0', focusedItem.$$('.more-vert-button').getAttribute('tabindex'));
focusedItem.focus();
keydown(focusedItem, 'ArrowDown');
focusedItem = items[1];
assertEquals('0', focusedItem.getAttribute('tabindex'));
assertEquals(
'0', focusedItem.$$('.more-vert-button').getAttribute('tabindex'));
assertDeepEquals(['3'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowUp');
focusedItem = items[0];
assertEquals('0', focusedItem.getAttribute('tabindex'));
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowRight');
focusedItem = items[0];
assertEquals(items[0], document.activeElement.root.activeElement);
assertEquals(items[0].$.menuButton, items[0].root.activeElement);
keydown(focusedItem, 'ArrowLeft');
focusedItem = items[0];
assertEquals(items[0], document.activeElement.root.activeElement);
assertEquals(null, items[0].root.activeElement);
keydown(focusedItem, 'End');
focusedItem = items[5];
assertEquals('0', focusedItem.getAttribute('tabindex'));
assertDeepEquals(['7'], normalizeIterable(store.data.selection.items));
// Moving past the end of the list is a no-op.
keydown(focusedItem, 'ArrowDown');
assertEquals('0', focusedItem.getAttribute('tabindex'));
assertDeepEquals(['7'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'Home');
focusedItem = items[0];
assertEquals('0', focusedItem.getAttribute('tabindex'));
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
// Moving past the start of the list is a no-op.
keydown(focusedItem, 'ArrowUp');
assertEquals('0', focusedItem.getAttribute('tabindex'));
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'Escape');
assertDeepEquals([], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'a', multiKey);
assertDeepEquals(
['2', '3', '4', '5', '6', '7'],
normalizeIterable(store.data.selection.items));
});
test('shift selection', function() {
let focusedItem = items[0];
focusedItem.focus();
keydown(focusedItem, 'ArrowDown', 'shift');
focusedItem = items[1];
assertDeepEquals(
['2', '3'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'Escape');
focusedItem = items[1];
assertDeepEquals([], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowUp', 'shift');
focusedItem = items[0];
assertDeepEquals(
['2', '3'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowDown', 'shift');
focusedItem = items[1];
assertDeepEquals(['3'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowDown', 'shift');
focusedItem = items[2];
assertDeepEquals(
['3', '4'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'End', 'shift');
focusedItem = items[2];
assertDeepEquals(
['3', '4', '5', '6', '7'],
normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'Home', 'shift');
focusedItem = items[2];
assertDeepEquals(
['2', '3'], normalizeIterable(store.data.selection.items));
});
test('ctrl selection', function() {
let focusedItem = items[0];
focusedItem.focus();
keydown(focusedItem, ' ', multiKey);
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowDown', multiKey);
focusedItem = items[1];
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
assertEquals('3', store.data.selection.anchor);
keydown(focusedItem, 'ArrowDown', multiKey);
focusedItem = items[2];
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, ' ', multiKey);
assertDeepEquals(
['2', '4'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, ' ', multiKey);
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
});
test('ctrl+shift selection', function() {
let focusedItem = items[0];
focusedItem.focus();
keydown(focusedItem, ' ', multiKey);
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowDown', multiKey);
focusedItem = items[1];
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowDown', multiKey);
focusedItem = items[2];
assertDeepEquals(['2'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowDown', [multiKey, 'shift']);
focusedItem = items[3];
assertDeepEquals(
['2', '4', '5'], normalizeIterable(store.data.selection.items));
keydown(focusedItem, 'ArrowDown', [multiKey, 'shift']);
focusedItem = items[3];
assertDeepEquals(
['2', '4', '5', '6'], normalizeIterable(store.data.selection.items));
});
test('keyboard commands are passed to command manager', function() {
chrome.bookmarkManagerPrivate.removeTrees = function() {};
store.data.selection.items = new Set(['2', '3']);
store.notifyObservers();
const focusedItem = items[4];
focusedItem.focus();
keydown(focusedItem, 'Delete');
// Commands should take affect on the selection, even if something else is
// focused.
commandManager.assertLastCommand(Command.DELETE, ['2', '3']);
});
test('iron-list does not steal focus on enter', function() {
// Iron-list attempts to focus the whole <bookmarks-item> when pressing
// enter on the menu button. This checks that we block this behavior
// during keydown on <bookmarks-list>.
const button = items[0].$$('.more-vert-button');
button.focus();
keydown(button, 'Enter');
assertEquals(button, items[0].root.activeElement);
});
});
suite('DialogFocusManager', function() {
let list;
let store;
let items;
let commandManager;
let dialogFocusManager;
function waitForClose(el) {
return new Promise(function(resolve) {
listenOnce(el, 'close', function(e) {
resolve();
});
});
}
function keydown(el, key) {
MockInteractions.keyDownOn(el, '', '', key);
}
setup(function() {
const nodes = testTree(createFolder('1', [
createItem('2'),
createItem('3'),
createItem('4'),
createItem('5'),
createItem('6'),
createFolder('7', []),
]));
store = new bookmarks.TestStore({
nodes: nodes,
folderOpenState: getAllFoldersOpenState(nodes),
selectedFolder: '1',
});
store.setReducersEnabled(true);
store.replaceSingleton();
list = document.createElement('bookmarks-list');
list.style.height = '100%';
list.style.width = '100%';
list.style.position = 'absolute';
replaceBody(list);
Polymer.dom.flush();
items = list.root.querySelectorAll('bookmarks-item');
commandManager = new TestCommandManager();
document.body.appendChild(commandManager);
dialogFocusManager = new bookmarks.DialogFocusManager();
bookmarks.DialogFocusManager.instance_ = dialogFocusManager;
});
test('restores focus on dialog dismissal', function() {
const focusedItem = items[0];
focusedItem.focus();
assertEquals(focusedItem, dialogFocusManager.getFocusedElement_());
commandManager.openCommandMenuAtPosition(0, 0);
const dropdown = commandManager.$.dropdown.getIfExists();
assertTrue(dropdown.open);
assertNotEquals(focusedItem, dialogFocusManager.getFocusedElement_());
keydown(dropdown, 'Escape');
assertFalse(dropdown.open);
return waitForClose(dropdown).then(() => {
assertEquals(focusedItem, dialogFocusManager.getFocusedElement_());
});
});
test('restores focus after stacked dialogs', function() {
const focusedItem = items[0];
focusedItem.focus();
assertEquals(focusedItem, dialogFocusManager.getFocusedElement_());
commandManager.openCommandMenuAtPosition(0, 0);
const dropdown = commandManager.$.dropdown.getIfExists();
dropdown.close();
assertNotEquals(focusedItem, dialogFocusManager.getFocusedElement_());
const editDialog = commandManager.$.editDialog.get();
editDialog.showEditDialog(store.data.nodes['2']);
return waitForClose(dropdown)
.then(() => {
editDialog.onCancelButtonTap_();
assertNotEquals(
focusedItem, dialogFocusManager.getFocusedElement_());
return waitForClose(editDialog);
})
.then(() => {
assertEquals(focusedItem, dialogFocusManager.getFocusedElement_());
});
});
test('restores focus after multiple shows of same dialog', function() {
let focusedItem = items[0];
focusedItem.focus();
assertEquals(focusedItem, dialogFocusManager.getFocusedElement_());
commandManager.openCommandMenuAtPosition(0, 0);
assertNotEquals(focusedItem, dialogFocusManager.getFocusedElement_());
const dropdown = commandManager.$.dropdown.getIfExists();
dropdown.close();
focusedItem = items[3];
focusedItem.focus();
commandManager.openCommandMenuAtPosition(0, 0);
return waitForClose(dropdown)
.then(() => {
assertTrue(dropdown.open);
dropdown.close();
assertNotEquals(
focusedItem, dialogFocusManager.getFocusedElement_());
return waitForClose(dropdown);
})
.then(() => {
assertEquals(focusedItem, dialogFocusManager.getFocusedElement_());
});
});
});
mocha.run();
});