blob: dcdfed5ef7c34c6a9bf2ac4217c9b04c503d5875 [file] [log] [blame]
// Copyright 2019 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.
'use strict';
(() => {
/**
* Send Tab keys to Files app until #file-list gets a selected item.
*
* Raise test failure if #file-list doesn't get anything selected in 20 tabs.
*
* NOTE:
* 1. Sends a maximum of 20 tabs.
* 2. The focused element after this function might NOT be #file-list, since
* updating the selected item which is async can be detected after an extra
* Tab has been sent.
*/
async function tabUntilFileSelected(appId) {
// Check: the file-list should have nothing selected.
const selectedRows = await remoteCall.callRemoteTestUtil(
'deepQueryAllElements', appId, ['#file-list li[selected]']);
chrome.test.assertEq(0, selectedRows.length);
// Press the tab until file-list gets focus and select some file.
for (let i = 0; i < 20; i++) {
// Send Tab key.
const result = await sendTestMessage({name: 'dispatchTabKey'});
chrome.test.assertEq(
result, 'tabKeyDispatched', 'Tab key dispatch failed');
// Check if there is a file selected, and return if so.
const selectedRows = await remoteCall.callRemoteTestUtil(
'deepQueryAllElements', appId, ['#file-list li[selected]']);
if (selectedRows.length > 0) {
return;
}
}
chrome.test.assertTrue(false, 'File list has no selection');
}
/**
* Tests that file list column header have ARIA attributes.
*/
testcase.fileListAriaAttributes = async () => {
const appId = await setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
// Fetch column header.
const columnHeadersQuery =
['#detail-table .table-header [aria-describedby]'];
const columnHeaders = await remoteCall.callRemoteTestUtil(
'deepQueryAllElements', appId, [columnHeadersQuery, ['display']]);
chrome.test.assertTrue(columnHeaders.length > 0);
for (const header of columnHeaders) {
// aria-describedby is used to tell users that they can click and which
// type of sort asc/desc will happen.
chrome.test.assertTrue('aria-describedby' in header.attributes);
// role button is used so users know that it's clickable.
chrome.test.assertEq('button', header.attributes.role);
}
};
/**
* Tests using tab to focus the file list will select the first item, if
* nothing is selected.
*/
testcase.fileListFocusFirstItem = async () => {
const appId = await setupAndWaitUntilReady(
RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []);
// Send Tab keys to make file selected.
await tabUntilFileSelected(appId);
// Check: The first file only is selected in the file entry rows.
const fileRows = await remoteCall.callRemoteTestUtil(
'deepQueryAllElements', appId, ['#file-list li']);
chrome.test.assertEq(5, fileRows.length);
const selectedRows = fileRows.filter(item => 'selected' in item.attributes);
chrome.test.assertEq(1, selectedRows.length);
chrome.test.assertEq(0, fileRows.indexOf(selectedRows[0]));
};
/**
* Tests that after a multiple selection, canceling the selection and using
* Tab to focus the files list it selects the item that was last focused.
*/
testcase.fileListSelectLastFocusedItem = async () => {
const appId = await setupAndWaitUntilReady(
RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []);
// Check: the file-list should have nothing selected.
let selectedRows = await remoteCall.callRemoteTestUtil(
'deepQueryAllElements', appId, ['#file-list li[selected]']);
chrome.test.assertEq(0, selectedRows.length);
// Move to item 2.
const downKey = ['#file-list', 'ArrowDown', false, false, false];
for (let i = 0; i < 2; i++) {
chrome.test.assertTrue(
!!await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, downKey),
'ArrowDown failed');
}
// Select item 2 with Ctrl+Space.
const ctrlSpace = ['#file-list', ' ', true, false, false];
chrome.test.assertTrue(
!!await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, ctrlSpace),
'Ctrl+Space failed');
// Move to item 3 and Cltr+Space to add it to multi-selection.
const ctrlDown = ['#file-list', 'ArrowDown', true, false, false];
chrome.test.assertTrue(
!!await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, ctrlDown),
'Ctrl+ArrowDown failed');
chrome.test.assertTrue(
!!await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, ctrlSpace),
'Ctrl+Space failed');
// Check that items 2 and 3 are selected.
selectedRows = await remoteCall.callRemoteTestUtil(
'deepQueryAllElements', appId, ['#file-list li[selected]']);
chrome.test.assertEq(2, selectedRows.length);
// Cancel the selection.
const button = ['#cancel-selection-button'];
await remoteCall.waitAndClickElement(appId, '#cancel-selection-button');
// Wait until selection is removed.
await remoteCall.waitForElementLost(appId, '#file-list li[selected]');
// Send Tab keys until a file item is selected.
await tabUntilFileSelected(appId);
// Check: The 3rd item only is selected.
const fileRows = await remoteCall.callRemoteTestUtil(
'deepQueryAllElements', appId, ['#file-list li']);
chrome.test.assertEq(5, fileRows.length);
selectedRows = fileRows.filter(item => 'selected' in item.attributes);
chrome.test.assertEq(1, selectedRows.length);
chrome.test.assertEq(2, fileRows.indexOf(selectedRows[0]));
};
/**
* Verifies the total number of a11y messages and asserts the latest message
* is the expected one.
*
* @param {string} appId
* @param {number} expectedCount
* @param {?string} expectedMessage
* @return {string} Latest a11y message.
*/
async function countAndCheckLatestA11yMessage(
appId, expectedCount, expectedMessage) {
const a11yMessages =
await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []);
if (expectedMessage === null || expectedMessage === undefined) {
return '';
}
const latestMessage = a11yMessages[a11yMessages.length - 1];
chrome.test.assertEq(
expectedCount, a11yMessages.length,
'Wrong number of a11y messages: latest message: ' + latestMessage);
chrome.test.assertEq(expectedMessage, latestMessage);
return latestMessage;
}
/**
* Tests that selecting/de-selecting files with keyboard produces a11y
* messages.
*
* NOTE: Test shared with grid_view.js.
* @param {boolean=} isGridView if the test is testing the grid view.
*/
testcase.fileListKeyboardSelectionA11y = async (isGridView) => {
const appId = await setupAndWaitUntilReady(
RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []);
let a11yMsgCount = 0;
const viewSelector = isGridView ? 'grid#file-list' : '#file-list';
if (isGridView) {
// Click view-button again to switch to detail view.
await remoteCall.waitAndClickElement(appId, '#view-button');
// Clicking #view-button adds 1 a11y message.
++a11yMsgCount;
}
// Keys used for keyboard navigation in the file list.
const homeKey = [viewSelector, 'Home', false, false, false];
const ctrlDownKey = [viewSelector, 'ArrowDown', true, false, false];
const ctrlSpaceKey = [viewSelector, ' ', true, false, false];
const shiftEndKey = [viewSelector, 'End', false, true, false];
const ctrlAKey = [viewSelector + ' li', 'a', true, false, false];
const escKey = [viewSelector, 'Escape', false, false, false];
// Select first item with Home key.
await remoteCall.fakeKeyDown(appId, ...homeKey);
// Navigating with Home key doesn't use aria-live message, it only uses the
// native listbox selection state messages.
await countAndCheckLatestA11yMessage(appId, a11yMsgCount);
// Ctrl+Down & Ctrl+Space to select second item: Beautiful Song.ogg
await remoteCall.fakeKeyDown(appId, ...ctrlDownKey);
await remoteCall.fakeKeyDown(appId, ...ctrlSpaceKey);
// Check: Announced "Beautiful Song.add" added to selection.
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount, 'Added Beautiful Song.ogg to selection.');
// Shift+End to select from 2nd item to the last item.
await remoteCall.fakeKeyDown(appId, ...shiftEndKey);
// Check: Announced range selection from "Beautiful Song.add" to hello.txt.
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount,
'Selected a range of 4 entries from Beautiful Song.ogg to hello.txt.');
// Ctrl+Space to de-select currently focused item (last item).
await remoteCall.fakeKeyDown(appId, ...ctrlSpaceKey);
// Check: Announced de-selecting hello.txt
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount, 'Removed hello.txt from selection.');
// Ctrl+A to select all items.
await remoteCall.fakeKeyDown(appId, ...ctrlAKey);
// Check: Announced selecting all entries.
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount, 'Selected all entries.');
// Esc key to deselect all.
await remoteCall.fakeKeyDown(appId, ...escKey);
// Check: Announced deselecting all entries.
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount, 'Removed all entries from selection.');
};
/**
* Tests that selecting/de-selecting files with mouse produces a11y messages.
*
* NOTE: Test shared with grid_view.js.
* @param {boolean=} isGridView if the test is testing the grid view.
*/
testcase.fileListMouseSelectionA11y = async (isGridView) => {
const appId = await setupAndWaitUntilReady(
RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []);
let a11yMsgCount = 0;
if (isGridView) {
// Click view-button again to switch to detail view.
await remoteCall.waitAndClickElement(appId, '#view-button');
// Clicking #view-button adds 1 a11y message.
++a11yMsgCount;
}
// Click first item.
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="photos"]');
// Simple click to select a file doesn't use aria-live message, it only
// uses the native listbox selection state messages.
await countAndCheckLatestA11yMessage(appId, a11yMsgCount);
// Ctrl+Click second item.
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="Beautiful Song.ogg"]', {ctrl: true});
// Check: Announced "Beautiful Song.add" added to selection.
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount, 'Added Beautiful Song.ogg to selection.');
// Shift+Click last item.
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="hello.txt"]', {shift: true});
// Check: Announced range selection from "Beautiful Song.add" to hello.txt.
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount,
'Selected a range of 4 entries from Beautiful Song.ogg to hello.txt.');
// Ctrl+Click to de-select the last item.
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="hello.txt"]', {ctrl: true});
// Check: Announced de-selecting hello.txt
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount, 'Removed hello.txt from selection.');
// Click on "Cancel selection" button.
await remoteCall.waitAndClickElement(appId, '#cancel-selection-button');
// Check: Announced deselecting all entries.
await countAndCheckLatestA11yMessage(
appId, ++a11yMsgCount, 'Removed all entries from selection.');
};
/**
* Tests the deletion of one or multiple items. After deletion, one of the
* remaining items should have the lead, but shouldn't be in check-select
* mode.
*/
testcase.fileListDeleteMultipleFiles = async () => {
const appId = await setupAndWaitUntilReady(
RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET, []);
// Select first 2 items.
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="world.ogv"]');
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="hello.txt"]', {shift: true});
// Delete item and confirm delete.
await remoteCall.waitAndClickElement(appId, '#delete-button');
await remoteCall.waitAndClickElement(
appId, '.files-confirm-dialog .cr-dialog-ok');
// Wait for completion of file deletion.
await remoteCall.waitForElementLost(
appId, '#file-list [file-name="world.ogv"]');
await remoteCall.waitForElementLost(
appId, '#file-list [file-name="hello.txt"]');
// Check: no selection state.
await remoteCall.waitForElementLost(appId, 'body.check-select');
// Check: lead state of last item.
let item = await remoteCall.waitForElement(
appId, '#file-list [file-name="My Desktop Background.png"]');
chrome.test.assertTrue('lead' in item.attributes);
// Check: selected state of last item.
chrome.test.assertTrue('selected' in item.attributes);
// Select and delete first item.
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="photos"]');
await remoteCall.waitAndClickElement(appId, '#delete-button');
await remoteCall.waitAndClickElement(
appId, '.files-confirm-dialog .cr-dialog-ok');
// Wait for file deletion.
await remoteCall.waitForElementLost(
appId, '#file-list [file-name="photos"]');
// Check: no selection state.
await remoteCall.waitForElementLost(appId, 'body.check-select');
// Check: lead state of first item.
item = await remoteCall.waitForElement(
appId, '#file-list [file-name="Beautiful Song.ogg"]');
chrome.test.assertTrue('lead' in item.attributes);
// Check: selected state of first item.
chrome.test.assertTrue('selected' in item.attributes);
};
})();