// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {type ElementObject} from '../prod/file_manager/shared_types.js';
import {addEntries, ENTRIES, getCaller, pending, repeatUntil, RootPath, sendTestMessage, TestEntryInfo} from '../test_util.js';
import {remoteCall} from './background.js';
import {DirectoryTreePageObject} from './page_objects/directory_tree.js';
import {BASIC_DRIVE_ENTRY_SET, BASIC_FAKE_ENTRY_SET, BASIC_LOCAL_ENTRY_SET, COMPUTERS_ENTRY_SET} from './test_data.js';
/**
* Waits for the empty folder element to show and assert the content to match
* the expected message.
* @param appId Files app windowId.
* @param expectedMessage The expected empty folder message
*/
async function waitForEmptyFolderMessage(
appId: string, expectedMessage: string) {
const caller = getCaller();
// Use repeatUntil() here because when we switch between different filters,
// the message changes but the element itself will always show there.
await repeatUntil(async () => {
const emptyMessage = await remoteCall.waitForElement(
appId, '#empty-folder:not(.hidden) > .label');
if (emptyMessage && emptyMessage.text?.startsWith(expectedMessage)) {
return;
}
return pending(
caller,
`Expected empty folder message: "${expectedMessage}", got "${
emptyMessage.text}"`);
});
}
/**
* Checks if the files initially added by the C++ side are displayed, and
* that files subsequently added are also displayed.
*
* @param path Path to be tested, Downloads or Drive.
* @param defaultEntries Default file entries.
*/
async function fileDisplay(path: string, defaultEntries: TestEntryInfo[]) {
// Open Files app on the given |path| with default file entries.
const appId = await remoteCall.setupAndWaitUntilReady(path);
// Verify the default file list is present in |result|.
const defaultList = TestEntryInfo.getExpectedRows(defaultEntries).sort();
await remoteCall.waitForFiles(appId, defaultList);
// Add new file entries.
await addEntries(['local', 'drive'], [ENTRIES.newlyAdded]);
// Verify the newly added entries appear in the file list.
const expectedList =
defaultList.concat([ENTRIES.newlyAdded.getExpectedRow()]);
await remoteCall.waitForFiles(appId, expectedList);
}
/**
* Tests files display in Downloads.
*/
export async function fileDisplayDownloads() {
return fileDisplay(RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET);
}
/**
* Tests opening the files app navigating to a local folder. Uses
* platform_util::OpenItem, a call to an API distinct from the one commonly used
* in other tests for the same operation.
*/
export async function fileDisplayLaunchOnLocalFolder() {
// Add a file to Downloads.
await addEntries(['local'], [ENTRIES.photos]);
// Open Files app on the Downloads directory.
await sendTestMessage(
{name: 'launchAppOnLocalFolder', localPath: 'Downloads/photos'});
// Wait for app window to open.
const appId = await remoteCall.waitForWindow();
// Check: The current directory is MyFiles/Downloads/photos.
await remoteCall.waitUntilCurrentDirectoryIsChanged(
appId, '/My files/Downloads/photos');
}
/**
* Tests opening the files app navigating to a local folder. Uses
* platform_util::OpenItem, a call to an API distinct from the one commonly used
* in other tests for the same operation.
*/
export async function fileDisplayLaunchOnLocalFile() {
// Add a file to Downloads.
await addEntries(['local'], [ENTRIES.hello, ENTRIES.world]);
// Open Files app on the Downloads directory selecting the target file.
await sendTestMessage(
{name: 'showItemInFolder', localPath: 'Downloads/hello.txt'});
// Wait for app window to open.
const appId = await remoteCall.waitForWindow();
// Check: The current directory is MyFiles/Downloads.
await remoteCall.waitUntilCurrentDirectoryIsChanged(
appId, '/My files/Downloads');
// Check: The target file is selected.
await remoteCall.waitForElement(
appId, '#file-list [file-name="hello.txt"][selected]');
}
/**
* Tests opening the files app navigating to the My Drive folder. Uses
* platform_util::OpenItem, a call to an API distinct from the one commonly used
* in other tests for the same operation.
*/
export async function fileDisplayLaunchOnDrive() {
// Open Files app on the Drive directory.
await sendTestMessage({name: 'launchAppOnDrive'});
// Wait for app window to open.
const appId = await remoteCall.waitForWindow();
// Check: the app should be open on My Drive.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.waitForSelectedItemByLabel('My Drive');
}
/**
* Tests files display in Google Drive.
*/
export async function fileDisplayDrive() {
return fileDisplay(RootPath.DRIVE, BASIC_DRIVE_ENTRY_SET);
}
/**
* Tests file display rendering in offline Google Drive.
*/
export async function fileDisplayDriveOffline() {
const driveFiles =
[ENTRIES.hello, ENTRIES.pinned, ENTRIES.photos, ENTRIES.testDocument];
// is not assignable to parameter of type 'TestEntryInfo[]'.
const appId =
await remoteCall.setupAndWaitUntilReady(RootPath.DRIVE, [], driveFiles);
// Retrieve all file list entries that could be rendered 'offline'.
// Use "first-child" here because opacity for offline only applies on the
// children elements.
const offlineEntry =
'#file-list .table-row.file.dim-offline > div:first-child';
let elements =
await remoteCall.queryElements(appId, offlineEntry, ['opacity']);
// Check: the hello.txt file only should be rendered 'offline'.
chrome.test.assertEq(1, elements.length);
chrome.test.assertEq(0, elements[0]!.text?.indexOf('hello.txt'));
// Check: hello.txt must have 'offline' CSS render style (opacity).
chrome.test.assertEq('0.38', elements[0]!.styles?.['opacity']);
// Retrieve file entries that are 'available offline' (not dimmed).
// Use "first-child" here because opacity for offline only applies on the
// children elements.
const availableEntry =
'#file-list .table-row:not(.dim-offline) > div:first-child';
elements = await remoteCall.queryElements(appId, availableEntry, ['opacity']);
// Check: these files should have 'available offline' CSS style.
chrome.test.assertEq(3, elements.length);
function checkRenderedInAvailableOfflineStyle(
element: ElementObject, fileName: string) {
chrome.test.assertEq(0, element.text!.indexOf(fileName));
chrome.test.assertEq('1', element.styles!['opacity']);
}
// Directories are shown as 'available offline'.
checkRenderedInAvailableOfflineStyle(elements[0]!, 'photos');
// Hosted documents are shown as 'available offline'.
checkRenderedInAvailableOfflineStyle(elements[1]!, 'Test Document.gdoc');
// Pinned files are shown as 'available offline'.
checkRenderedInAvailableOfflineStyle(elements[2]!, 'pinned');
}
/**
* Tests file display rendering in online Google Drive.
* @param appId the id for the window to check the file display.
*/
async function checkDriveOnlineDisplay(appId: string) {
// Retrieve all file list row entries.
const fileEntry = '#file-list .table-row';
const elements =
await remoteCall.queryElements(appId, fileEntry, ['opacity']);
// Check: all files must have 'online' CSS style (not dimmed).
chrome.test.assertEq(BASIC_DRIVE_ENTRY_SET.length, elements.length);
for (const element of elements) {
chrome.test.assertEq('1', element.styles?.['opacity']);
}
}
/**
* Tests file display rendering in online Google Drive.
*/
export async function fileDisplayDriveOnline() {
// Open Files app on Drive.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DRIVE, [], BASIC_DRIVE_ENTRY_SET);
await checkDriveOnlineDisplay(appId);
}
/**
* Tests file display rendering in online Google Drive when opening via OpenItem
* function.
*/
export async function fileDisplayDriveOnlineNewWindow() {
// Open Files app on the Drive directory.
await addEntries(['drive'], BASIC_DRIVE_ENTRY_SET);
await sendTestMessage({name: 'launchAppOnDrive'});
// Wait for app window to open.
const appId = await remoteCall.waitForWindow();
// Wait for Files app to finish loading.
await remoteCall.waitFor('isFileManagerLoaded', appId, true);
await checkDriveOnlineDisplay(appId);
}
/**
* Tests files display in the "Computers" section of Google Drive. Testing that
* we can navigate to folders inside /Computers also has the side effect of
* testing that the breadcrumbs are working.
*/
export async function fileDisplayComputers() {
// Open Files app on Drive with Computers registered.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DRIVE, [], COMPUTERS_ENTRY_SET);
// Navigate to Computer Grand Root.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.navigateToPath('/Computers');
// Navigate to a Computer Root.
await directoryTree.navigateToPath('/Computers/Computer A');
// Navigate to a subdirectory under a Computer Root.
await directoryTree.navigateToPath('/Computers/Computer A/A');
}
/**
* Tests files display in an MTP volume.
*/
export async function fileDisplayMtp() {
const MTP_VOLUME_TYPE = 'mtp';
// Open Files app on local downloads.
const appId = await remoteCall.setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Mount MTP volume in the Downloads window.
await sendTestMessage({name: 'mountFakeMtp'});
// Wait for the MTP mount and click to open the MTP volume.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectItemByType(MTP_VOLUME_TYPE);
// Verify the MTP file list.
const files = TestEntryInfo.getExpectedRows(BASIC_FAKE_ENTRY_SET);
await remoteCall.waitForFiles(appId, files, {ignoreLastModifiedTime: true});
}
/**
* Tests files display in a removable USB volume.
*/
export async function fileDisplayUsb() {
const USB_VOLUME_TYPE = 'removable';
// Open Files app on local downloads.
const appId = await remoteCall.setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Mount USB volume in the Downloads window.
await sendTestMessage({name: 'mountFakeUsb'});
// Wait for the USB mount and click to open the USB volume.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectItemByType(USB_VOLUME_TYPE);
// Verify the USB file list.
const files = TestEntryInfo.getExpectedRows(BASIC_FAKE_ENTRY_SET);
await remoteCall.waitForFiles(appId, files, {ignoreLastModifiedTime: true});
}
/**
* Tests files display on a removable USB volume with and without partitions.
*/
export async function fileDisplayUsbPartition() {
// Open Files app on local downloads.
const appId = await remoteCall.setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Mount USB device containing partitions.
await sendTestMessage({name: 'mountUsbWithPartitions'});
// Mount unpartitioned USB device.
await sendTestMessage({name: 'mountFakeUsb'});
// Wait for removable root to appear in the directory tree.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.waitForItemByLabel('Drive Label');
// Wait for removable partition-1 to appear in the directory tree.
await directoryTree.expandTreeItemByLabel('Drive Label');
const partitionOne = await directoryTree.waitForItemByLabel('partition-1');
chrome.test.assertEq(
'removable', directoryTree.getItemVolumeType(partitionOne));
// Wait for removable partition-2 to appear in the directory tree.
const partitionTwo = await directoryTree.waitForItemByLabel('partition-2');
chrome.test.assertEq(
'removable', directoryTree.getItemVolumeType(partitionTwo));
// Check partitions are children of the root label.
const childEntries =
await directoryTree.getChildItemsByParentLabel('Drive Label');
const childEntryLabels =
childEntries.map(child => directoryTree.getItemLabel(child));
chrome.test.assertEq(['partition-1', 'partition-2'], childEntryLabels);
if (await remoteCall.isSinglePartitionFormat(appId)) {
// Wait for USB to appear in the directory tree.
await directoryTree.waitForItemByLabel('FAKEUSB');
// Expand it before checking children items.
await directoryTree.expandTreeItemByLabel('FAKEUSB');
// Check unpartitioned USB has single partition as tree child.
const itemEntries =
await directoryTree.getChildItemsByParentLabel('FAKEUSB');
chrome.test.assertEq(1, itemEntries.length);
const childVolumeType = directoryTree.getItemVolumeType(itemEntries[0]!);
chrome.test.assertTrue('removable' === childVolumeType);
} else {
// Wait for USB to appear in the directory tree.
const fakeUsb = await directoryTree.waitForItemByLabel('fake-usb');
chrome.test.assertEq('removable', directoryTree.getItemVolumeType(fakeUsb));
// Expand it before checking children items.
await directoryTree.expandTreeItemByLabel('fake-usb');
// Check unpartitioned USB does not have partitions as tree children.
const itemEntries =
await directoryTree.getChildItemsByParentLabel('fake-usb');
chrome.test.assertEq(1, itemEntries.length);
const childVolumeType = directoryTree.getItemVolumeType(itemEntries[0]!);
chrome.test.assertTrue('removable' !== childVolumeType);
}
}
/**
* Tests that the file system type is properly displayed in the type
* column. Checks that the entries can be properly sorted by type.
* crbug.com/973743
*/
export async function fileDisplayUsbPartitionSort() {
// Open Files app on local downloads.
const appId = await remoteCall.setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Mount removable device with partitions.
await sendTestMessage({name: 'mountUsbWithMultiplePartitionTypes'});
// Wait and select the removable group by clicking the label.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectGroupRootItemByType('removable');
// Wait for partitions to appear in the file table list.
let expectedRows = [
['partition-3', '--', 'vfat'],
['partition-2', '--', 'ext4'],
['partition-1', '--', 'ntfs'],
];
const options = {orderCheck: true, ignoreLastModifiedTime: true};
await remoteCall.waitForFiles(appId, expectedRows, options);
// Sort by type in ascending order.
await remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['.table-header-cell:nth-of-type(3)']);
const iconSortedAsc =
'.table-header-cell .sorted [iron-icon="files16:arrow_up_small"]';
await remoteCall.waitForElement(appId, iconSortedAsc);
// Check that partitions are sorted in ascending order based on the partition
// type.
expectedRows = [
['partition-2', '--', 'ext4'],
['partition-1', '--', 'ntfs'],
['partition-3', '--', 'vfat'],
];
await remoteCall.waitForFiles(appId, expectedRows, options);
// Sort by type in descending order.
await remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['.table-header-cell:nth-of-type(3)']);
const iconSortedDesc =
'.table-header-cell .sorted [iron-icon="files16:arrow_down_small"]';
await remoteCall.waitForElement(appId, iconSortedDesc);
// Check that partitions are sorted in descending order based on the partition
// type.
expectedRows = [
['partition-3', '--', 'vfat'],
['partition-1', '--', 'ntfs'],
['partition-2', '--', 'ext4'],
];
await remoteCall.waitForFiles(appId, expectedRows, options);
}
/**
* Tests display of partitions in file list after mounting a removable USB
* volume.
*/
export async function fileDisplayPartitionFileTable() {
// Open Files app on local downloads.
const appId = await remoteCall.setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Mount removable partitions.
await sendTestMessage({name: 'mountUsbWithPartitions'});
// Wait for removable group to appear in the directory tree and select the
// first removable group by clicking the label.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectGroupRootItemByType('removable');
// Wait for removable partitions to appear in the file table.
const partitionOne = await remoteCall.waitForElement(
appId, '#file-list [file-name="partition-1"] .type');
chrome.test.assertEq('ext4', partitionOne.text);
const partitionTwo = await remoteCall.waitForElement(
appId, '#file-list [file-name="partition-2"] .type');
chrome.test.assertEq('ext4', partitionTwo.text);
}
/**
* Searches for a string in Downloads and checks that the correct results
* are displayed.
*
* @param searchTerm The string to search for.
* @param expectedResults The results set.
*
*/
async function searchDownloads(
searchTerm: string, expectedResults: TestEntryInfo[]) {
// Open Files app on local downloads.
const appId = await remoteCall.setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Focus the search box.
chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
'fakeEvent', appId, ['#search-box cr-input', 'focus']));
// Input a text.
await remoteCall.inputText(appId, '#search-box cr-input', searchTerm);
// Notify the element of the input.
chrome.test.assertTrue(!!await remoteCall.callRemoteTestUtil(
'fakeEvent', appId, ['#search-box cr-input', 'input']));
await remoteCall.waitForFiles(
appId, TestEntryInfo.getExpectedRows(expectedResults));
}
/**
* Tests case-sensitive search for an entry in Downloads.
*/
export async function fileSearch() {
return searchDownloads('hello', [ENTRIES.hello]);
}
/**
* Tests case-insenstive search for an entry in Downloads.
*/
export async function fileSearchCaseInsensitive() {
return searchDownloads('HELLO', [ENTRIES.hello]);
}
/**
* Tests searching for a string doesn't match anything in Downloads and that
* there are no displayed items that match the search string.
*/
export async function fileSearchNotFound() {
const searchTerm = 'blahblah';
const appId = await remoteCall.setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Focus the search box.
chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
'fakeEvent', appId, ['#search-box cr-input', 'focus']));
// Input a text.
await remoteCall.inputText(appId, '#search-box cr-input', searchTerm);
// Notify the element of the input.
await remoteCall.callRemoteTestUtil(
'fakeEvent', appId, ['#search-box cr-input', 'input']);
await remoteCall.waitForFiles(appId, []);
}
/**
* Tests Files app opening without errors when there isn't Downloads which is
* the default volume.
*/
export async function fileDisplayWithoutDownloadsVolume() {
// Ensure no volumes are mounted.
chrome.test.assertEq('0', await sendTestMessage({name: 'getVolumesCount'}));
// Mount Drive.
await sendTestMessage({name: 'mountDrive'});
await remoteCall.waitForVolumesCount(1);
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Wait for Files app to finish loading.
await remoteCall.waitFor('isFileManagerLoaded', appId, true);
}
/**
* Tests Files app opening without errors when there are no volumes at all.
*/
export async function fileDisplayWithoutVolumes() {
// Ensure no volumes are mounted.
chrome.test.assertEq('0', await sendTestMessage({name: 'getVolumesCount'}));
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Wait for Files app to finish loading.
await remoteCall.waitFor('isFileManagerLoaded', appId, true);
}
/**
* Tests Files app opening without errors when there are no volumes at all and
* then mounting Downloads volume which should appear and be able to display its
* files.
*/
export async function fileDisplayWithoutVolumesThenMountDownloads() {
// Ensure no volumes are mounted.
chrome.test.assertEq('0', await sendTestMessage({name: 'getVolumesCount'}));
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Wait for Files app to finish loading.
await remoteCall.waitFor('isFileManagerLoaded', appId, true);
// Mount Downloads.
await sendTestMessage({name: 'mountDownloads'});
// Wait until Downloads is mounted.
await remoteCall.waitFor('getVolumesCount', null, (count) => count === 1, []);
// Downloads should appear in My files in the directory tree.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.waitForItemByLabel('My files');
const expectedRows =
[['Downloads', '--', 'Folder'], ['Linux files', '--', 'Folder']];
await remoteCall.waitForFiles(
appId, expectedRows,
{ignoreFileSize: true, ignoreLastModifiedTime: true});
}
/**
* Tests Files app opening without errors when there are no volumes at all and
* then mounting Drive volume which should appear and be able to display its
* files.
*/
export async function fileDisplayWithoutVolumesThenMountDrive() {
// Ensure no volumes are mounted.
chrome.test.assertEq('0', await sendTestMessage({name: 'getVolumesCount'}));
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Wait for Files app to finish loading.
await remoteCall.waitFor('isFileManagerLoaded', appId, true);
// Navigate to the Drive FakeItem.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectGroupRootItemByType('drive');
// The fake Google Drive should be empty.
await remoteCall.waitForFiles(appId, []);
// Remount Drive. The curent directory should be changed from the Google
// Drive FakeItem to My Drive.
await sendTestMessage({name: 'mountDrive'});
// Wait until Drive is mounted.
await remoteCall.waitFor('getVolumesCount', null, (count) => count === 1, []);
// Add an entry to Drive.
await addEntries(['drive'], [ENTRIES.newlyAdded]);
// Wait for "My Drive" files to display in the file list.
await remoteCall.waitForFiles(appId, [ENTRIES.newlyAdded.getExpectedRow()]);
}
/**
* Tests Files app opening without Drive mounted.
*/
export async function fileDisplayWithoutDrive() {
// Ensure no volumes are mounted.
chrome.test.assertEq('0', await sendTestMessage({name: 'getVolumesCount'}));
// Mount Downloads.
await sendTestMessage({name: 'mountDownloads'});
// Wait until downloads is re-added
await remoteCall.waitForVolumesCount(1);
// Open the files app.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.newlyAdded], []);
// Wait for the loading indicator blink to finish.
await remoteCall.waitForElement(
appId, '#list-container .loading-indicator[hidden]');
// Navigate to the fake Google Drive.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectGroupRootItemByType('drive');
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Google Drive');
// Check that the scanner have finished.
await remoteCall.waitForElement(appId, `[scan-completed="Google Drive"]`);
// Check: the fake Google Drive should be empty.
await remoteCall.waitForFiles(appId, []);
}
/**
* Tests Files app opening without Drive mounted and then disabling and
* re-enabling Drive.
*/
export async function fileDisplayWithoutDriveThenDisable() {
// Ensure no volumes are mounted.
chrome.test.assertEq('0', await sendTestMessage({name: 'getVolumesCount'}));
// Mount Downloads.
await sendTestMessage({name: 'mountDownloads'});
// Add a file to Downloads.
await addEntries(['local'], [ENTRIES.newlyAdded]);
// Wait until it mounts.
await remoteCall.waitForVolumesCount(1);
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Wait for Files app to finish loading.
await remoteCall.waitFor('isFileManagerLoaded', appId, true);
// We should navigate to MyFiles.
const expectedRows = [
['Downloads', '--', 'Folder'],
['Linux files', '--', 'Folder'],
];
await remoteCall.waitForFiles(
appId, expectedRows, {ignoreLastModifiedTime: true});
// Navigate to Drive.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectGroupRootItemByType('drive');
// The fake Google Drive should be empty.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Google Drive');
await remoteCall.waitForFiles(appId, []);
// Disable Drive.
await sendTestMessage({name: 'setDriveEnabled', enabled: false});
await directoryTree.waitForItemLostByLabel('Google Drive');
// The current directory should change to the default (Downloads).
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// Ensure Downloads has loaded.
await remoteCall.waitForFiles(
appId, expectedRows, {ignoreLastModifiedTime: true});
// Re-enabled Drive.
await sendTestMessage({name: 'setDriveEnabled', enabled: true});
// Wait for the fake drive to reappear.
await directoryTree.waitForItemByLabel('Google Drive');
}
/**
* Tests that mounting a hidden Volume does not mount the volume in file
* manager.
*/
export async function fileDisplayWithHiddenVolume() {
const initialVolumeCount = await sendTestMessage({name: 'getVolumesCount'});
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
// Get the directory tree elements.
const directoryTree = await DirectoryTreePageObject.create(appId);
const visibleLabelsBefore = await directoryTree.getVisibleItemLabels();
// Mount a hidden volume.
await sendTestMessage({name: 'mountHidden'});
const visibleLabelsAfter = await directoryTree.getVisibleItemLabels();
// The directory tree should NOT display the hidden volume.
chrome.test.assertEq(visibleLabelsBefore, visibleLabelsAfter);
// The hidden volume should not be counted in the number of volumes.
chrome.test.assertEq(
initialVolumeCount, await sendTestMessage({name: 'getVolumesCount'}));
}
/**
* Tests Files app resisting the urge to switch to Downloads when mounts change.
*/
export async function fileDisplayMountWithFakeItemSelected() {
// Open Files app on Drive with the given test files.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.newlyAdded], []);
// Ensure Downloads has loaded.
await remoteCall.waitForFiles(appId, [ENTRIES.newlyAdded.getExpectedRow()]);
// Navigate to My files.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectItemByLabel('My files');
// Wait for the navigation to complete.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// Mount a USB drive.
await sendTestMessage({name: 'mountFakeUsbEmpty'});
// Wait for the mount to appear.
await directoryTree.waitForItemByType('removable');
chrome.test.assertEq(
'/My files',
await remoteCall.callRemoteTestUtil('getBreadcrumbPath', appId, []));
}
/**
* Tests Files app switching away from Drive virtual folders when Drive is
* unmounted.
*/
export async function fileDisplayUnmountDriveWithSharedWithMeSelected() {
// Open Files app on Drive with the given test files.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DRIVE, [ENTRIES.newlyAdded],
[ENTRIES.testSharedDocument, ENTRIES.hello]);
// Navigate to Shared with me.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectItemByLabel('Shared with me');
// Wait for the navigation to complete.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Shared with me');
// Check that the file is visible.
await remoteCall.waitForFiles(
appId, [ENTRIES.testSharedDocument.getExpectedRow()]);
// Unmount drive.
await sendTestMessage({name: 'unmountDrive'});
// We should navigate to MyFiles.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// Which should contain a file.
const expectedRows = [
['Play files', '--', 'Folder'],
['Downloads', '--', 'Folder'],
['Linux files', '--', 'Folder'],
];
await remoteCall.waitForFiles(
appId, expectedRows, {ignoreLastModifiedTime: true});
}
/**
* Navigates to a removable volume, then unmounts it. Check to see whether
* Files App switches away to the default Downloads directory.
*
* @param removableDirectory The removable directory to be inside
* before unmounting the USB.
*/
async function unmountRemovableVolume(removableDirectory: string) {
// Open Files app on Downloads containing ENTRIES.photos.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.photos], []);
// Mount a device containing two partitions.
await sendTestMessage({name: 'mountUsbWithPartitions'});
// Wait for the removable root to appear in the directory tree and navigate to
// the removable root directory.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectGroupRootItemByType('removable');
// Wait for the navigation to complete.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Drive Label');
// Wait for partition entries to appear in the directory.
await remoteCall.waitForElement(
appId, '#file-list [file-name="partition-1"]');
await remoteCall.waitForElement(
appId, '#file-list [file-name="partition-2"]');
if (removableDirectory === 'partition-1' ||
removableDirectory === 'partition-2') {
const partitionQuery = `#file-list [file-name="${removableDirectory}"]`;
const partitionFiles = TestEntryInfo.getExpectedRows(BASIC_FAKE_ENTRY_SET);
await remoteCall.callRemoteTestUtil(
'fakeMouseDoubleClick', appId, [partitionQuery]);
await remoteCall.waitUntilCurrentDirectoryIsChanged(
appId, `/Drive Label/${removableDirectory}`);
await remoteCall.waitForFiles(
appId, partitionFiles, {ignoreLastModifiedTime: true});
}
// Unmount partitioned device.
await sendTestMessage({name: 'unmountPartitions'});
// We should navigate to MyFiles.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// And contains the expected files.
const expectedRows = [
['Play files', '--', 'Folder'],
['Downloads', '--', 'Folder'],
['Linux files', '--', 'Folder'],
];
await remoteCall.waitForFiles(
appId, expectedRows, {ignoreLastModifiedTime: true});
}
/**
* Tests Files app switches away from a removable device root after the USB is
* unmounted.
*/
export async function fileDisplayUnmountRemovableRoot() {
return unmountRemovableVolume('Drive Label');
}
/**
* Tests Files app switches away from a partition inside the USB after the USB
* is unmounted.
*/
export async function fileDisplayUnmountFirstPartition() {
return unmountRemovableVolume('partition-1');
}
/**
* Tests Files app switches away from a partition inside the USB after the USB
* is unmounted. Partition-1 will be ejected first.
*/
export async function fileDisplayUnmountLastPartition() {
return unmountRemovableVolume('partition-2');
}
/**
* Tests files display in Downloads while the default blocking file I/O task
* runner is blocked.
*/
export async function fileDisplayDownloadsWithBlockedFileTaskRunner() {
await sendTestMessage({name: 'blockFileTaskRunner'});
await fileDisplay(RootPath.DOWNLOADS, BASIC_LOCAL_ENTRY_SET);
await sendTestMessage({name: 'unblockFileTaskRunner'});
}
/**
* Tests to make sure check-select mode enables when selecting one item
*/
export async function fileDisplayCheckSelectWithFakeItemSelected() {
// Open files app on Downloads containing ENTRIES.hello.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.hello], []);
// Select ENTRIES.hello.
await remoteCall.waitUntilSelected(appId, ENTRIES.hello.nameText);
// Select all.
const ctrlA = ['#file-list', 'a', true, false, false];
chrome.test.assertTrue(
await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, ctrlA));
// Make sure check-select is enabled.
await remoteCall.waitForElement(appId, 'body.check-select');
}
/**
* Tests to make sure read-only indicator is visible when the current directory
* is read-only.
*/
export async function fileDisplayCheckReadOnlyIconOnFakeDirectory() {
// Open Files app on Drive with the given test files.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DRIVE, [ENTRIES.newlyAdded],
[ENTRIES.testSharedDocument, ENTRIES.hello]);
// Navigate to Shared with me.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectItemByLabel('Shared with me');
// Wait for the navigation to complete.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Shared with me');
// Make sure read-only indicator on toolbar is visible.
await remoteCall.waitForElement(appId, '#read-only-indicator:not([hidden])');
}
/**
* Tests to make sure read-only indicator is NOT visible when the current
* is writable.
*/
export async function fileDisplayCheckNoReadOnlyIconOnDownloads() {
// Open files app on Downloads containing ENTRIES.hello.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.hello], []);
// Make sure read-only indicator on toolbar is NOT visible.
await remoteCall.waitForElement(appId, '#read-only-indicator[hidden]');
}
/**
* Tests to make sure read-only indicator is NOT visible when the current
* directory is the "Linux files" fake root.
*/
export async function fileDisplayCheckNoReadOnlyIconOnLinuxFiles() {
// Block mounts from progressing. This should cause the file manager to always
// show the loading bar for linux files.
await sendTestMessage({name: 'blockMounts'});
// Open files app on Downloads.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.hello], []);
// Click on Linux files.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectPlaceholderItemByType('crostini');
// Check: the loading indicator should be visible.
await remoteCall.waitForElement(
appId, '#list-container .loading-indicator:not([hidden])');
// Check: the toolbar read-only indicator should not be visible.
await remoteCall.waitForElement(appId, '#read-only-indicator[hidden]');
}
/**
* Tests to make sure read-only indicator is NOT visible when the current
* directory is a "GuestOs" fake root.
*/
export async function fileDisplayCheckNoReadOnlyIconOnGuestOs() {
// Create a Bruschetta guest for this test.
await sendTestMessage({
name: 'registerMountableGuest',
displayName: 'mogsaur',
canMount: true,
vmType: 'bruschetta',
});
// Block mounts from progressing. This should cause the file manager to always
// show the loading bar for our mount.
await sendTestMessage({name: 'blockMounts'});
// Open files app on Downloads.
const appId = await remoteCall.setupAndWaitUntilReady(
RootPath.DOWNLOADS, [ENTRIES.hello], []);
// Click on the placeholder.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectPlaceholderItemByType('bruschetta');
// Check: the loading indicator should be visible.
await remoteCall.waitForElement(
appId, '#list-container .loading-indicator:not([hidden])');
// Check: the toolbar read-only indicator should not be visible.
await remoteCall.waitForElement(appId, '#read-only-indicator[hidden]');
}
/**
* Tests that when local files are disabled, we navigate to default set by the
* policy, e.g. Drive after unmounting a USB.
*/
export async function fileDisplayLocalFilesDisabledUnmountRemovable() {
// Mount Drive and Downloads.
await sendTestMessage({name: 'mountDrive'});
await sendTestMessage({name: 'mountDownloads'});
// Ensure two volumes are mounted.
await remoteCall.waitForVolumesCount(2);
// Enable SkyVault.
await sendTestMessage({name: 'setLocalFilesEnabled', enabled: false});
await sendTestMessage(
{name: 'setDefaultLocation', defaultLocation: 'google_drive'});
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Confirm that the Files App opened in Google Drive, as set by the policy.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My Drive');
// Mount USB volume in the Downloads window.
await sendTestMessage({name: 'mountFakeUsb'});
// Wait for the USB mount and click to open the USB volume.
const directoryTree = await DirectoryTreePageObject.create(appId);
await directoryTree.selectItemByType('removable');
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/fake-usb');
// Unmount the USB.
await sendTestMessage({name: 'unmountUsb'});
// We should navigate to My Drive.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My Drive');
}
/**
* Tests that disabling local storage while in a local folder navigates away to
* the default set by the policy, e.g. Drive.
*/
export async function fileDisplayLocalFilesDisableInMyFiles() {
// Mount Drive and Downloads.
await sendTestMessage({name: 'mountDrive'});
await sendTestMessage({name: 'mountDownloads'});
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Confirm that the Files App opened in MyFiles.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// Disable local storage and set Google Drive as the default.
await sendTestMessage({name: 'setLocalFilesEnabled', enabled: false});
await sendTestMessage(
{name: 'setDefaultLocation', defaultLocation: 'google_drive'});
// We should navigate to Drive.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My Drive');
}
/**
* Tests that disabling local storage while in a local folder navigates away to
* the default set by the policy, e.g. Drive.
*/
export async function fileDisplayOneDrivePlaceholder() {
// Mount Downloads.
await sendTestMessage({name: 'mountDownloads'});
// Set OneDrive as the default. Must be done before the dialog is opened.
await sendTestMessage(
{name: 'setDefaultLocation', defaultLocation: 'microsoft_onedrive'});
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Confirm that the Files App opened in MyFiles.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// Confirm that OneDrive isn't shown yet.
const directoryTree = await DirectoryTreePageObject.create(appId);
const oneDriveLabel = 'Microsoft OneDrive';
directoryTree.waitForItemLostByLabel(oneDriveLabel);
// Disable local storage.
await sendTestMessage({name: 'setLocalFilesEnabled', enabled: false});
directoryTree.waitForItemLostByLabel('/My files');
// Check that the placeholder is added.
await directoryTree.waitForItemByLabel(oneDriveLabel);
// Files App should open by default in the placeholder.
await remoteCall.waitUntilCurrentDirectoryIsChanged(
appId, `/${oneDriveLabel}`);
// Check: the empty folder should be visible.
await remoteCall.waitForElement(appId, '#empty-folder:not([hidden])');
await waitForEmptyFolderMessage(appId, 'You\'ve been logged out');
}
/**
* Tests that having no volumes (other than Recent), like when SkyVault is
* misconfigured (no local storage, no cloud selected as alternative) shows an
* error message.
*/
export async function fileDisplayFileSystemDisabled() {
// Disable local storage.
await sendTestMessage({name: 'setLocalFilesEnabled', enabled: false});
// Disable drive.
await sendTestMessage({name: 'setDriveEnabled', enabled: false});
// Skip SkyVault migration to immediately remove My Files.
await sendTestMessage({name: 'skipSkyVaultMigration'});
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
// Check: the empty folder should be visible.
await remoteCall.waitForElement(appId, '#empty-folder:not([hidden])');
await waitForEmptyFolderMessage(
appId, 'The file system has been disabled by your administrator');
// Mount USB volume.
await sendTestMessage({name: 'mountFakeUsb'});
// Check: the empty folder should hide.
await remoteCall.waitForElement(appId, '#empty-folder[hidden]');
}
/**
* Tests that the files migrating to cloud banner appears when MyFiles is opened
* while SkyVault migration is enabled.
*/
export async function fileDisplaySkyVaultMigrationToGoogleDrive() {
await remoteCall.setLocalFilesMigrationDestination('google_drive');
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
await remoteCall.isolateBannerForTesting(
appId, 'files-migrating-to-cloud-banner');
// We should navigate to MyFiles.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// Check: the migration banner should be visible.
await remoteCall.waitForElement(
appId, '#banners > files-migrating-to-cloud-banner');
}
/**
* Tests that the files migrating to cloud banner appears when MyFiles is opened
* while SkyVault migration is enabled.
*/
export async function fileDisplaySkyVaultMigrationToOneDrive() {
await remoteCall.setLocalFilesMigrationDestination('microsoft_onedrive');
// Open Files app without specifying the initial directory/root.
const appId = await remoteCall.openNewWindow(null, null);
chrome.test.assertTrue(!!appId, 'failed to open new window');
await remoteCall.isolateBannerForTesting(
appId, 'files-migrating-to-cloud-banner');
// We should navigate to MyFiles.
await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/My files');
// Check: the migration banner should be visible.
await remoteCall.waitForElement(
appId, '#banners > files-migrating-to-cloud-banner');
}