| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import {FilesAppState} from '../files_app_state.js'; |
| import {addEntries, ENTRIES, EntryType, getCaller, getDateWithDayDiff, pending, repeatUntil, RootPath, sendTestMessage, SharedOption, TestEntryInfo} from '../test_util.js'; |
| import {testcase} from '../testcase.js'; |
| |
| import {mountCrostini, navigateWithDirectoryTree, remoteCall, setupAndWaitUntilReady} from './background.js'; |
| import {BASIC_ANDROID_ENTRY_SET, BASIC_DRIVE_ENTRY_SET, BASIC_FAKE_ENTRY_SET, BASIC_LOCAL_ENTRY_SET, NESTED_ENTRY_SET} from './test_data.js'; |
| |
| /** |
| * @param {string} appId The ID that identifies the files app. |
| * @param {string} type The search option type (location, recency, type). |
| * @return {Promise<string>} The text of the element with 'selected-option' ID. |
| */ |
| async function getSelectedOptionText(appId, type) { |
| // Force refresh of the element by showing the dropdown menu. |
| await remoteCall.callRemoteTestUtil('fakeMouseClick', appId, [ |
| [ |
| 'xf-search-options', |
| `xf-select#${type}-selector`, |
| ], |
| ]); |
| // Fetch the current selected item. |
| const option = await remoteCall.waitForElement(appId, [ |
| 'xf-search-options', |
| `xf-select#${type}-selector`, |
| '#selected-option', |
| ]); |
| return option.text; |
| } |
| |
| /** |
| * Tests searching inside Downloads with results. |
| */ |
| testcase.searchDownloadsWithResults = async () => { |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Search for all files with "hello" in their name. |
| await remoteCall.typeSearchText(appId, 'hello'); |
| |
| // Wait file list to display the search result. |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([ENTRIES.hello])); |
| |
| // Check that a11y message for results has been issued. |
| const a11yMessages = |
| await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []); |
| chrome.test.assertEq(1, a11yMessages.length, 'Missing a11y message'); |
| chrome.test.assertEq('Showing results for hello.', a11yMessages[0]); |
| |
| return appId; |
| }; |
| |
| /** |
| * Tests searching inside Downloads without results. |
| */ |
| testcase.searchDownloadsWithNoResults = async () => { |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Search for name not present among basic entry set. |
| await remoteCall.typeSearchText(appId, 'INVALID TERM'); |
| |
| // Wait file list to display no results. |
| await remoteCall.waitForFiles(appId, []); |
| |
| // Check that a11y message for no results has been issued. |
| const a11yMessages = |
| await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []); |
| chrome.test.assertEq(1, a11yMessages.length, 'Missing a11y message'); |
| chrome.test.assertEq( |
| 'There are no results for INVALID TERM.', a11yMessages[0]); |
| }; |
| |
| /** |
| * Tests that clearing the search box announces the A11y. |
| */ |
| testcase.searchDownloadsClearSearch = async () => { |
| // Perform a normal search, to be able to clear the search box. |
| const appId = await testcase.searchDownloadsWithResults(); |
| |
| // Click on the clear search button. |
| await remoteCall.waitAndClickElement(appId, '#search-box .clear'); |
| |
| // Wait for the search box to fully collapse. |
| await remoteCall.waitForElement(appId, '#search-wrapper[collapsed]'); |
| |
| // Wait for file list to display all files. |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows(BASIC_LOCAL_ENTRY_SET)); |
| |
| // Check that a11y message for clearing the search term has been issued. |
| const a11yMessages = |
| await remoteCall.callRemoteTestUtil('getA11yAnnounces', appId, []); |
| chrome.test.assertEq( |
| 2, a11yMessages.length, |
| `Want 2 messages got ${JSON.stringify(a11yMessages)}`); |
| chrome.test.assertEq( |
| 'Search text cleared, showing all files and folders.', a11yMessages[1]); |
| }; |
| |
| /** |
| * Tests that clearing the search box with keydown crbug.com/910068. |
| */ |
| testcase.searchDownloadsClearSearchKeyDown = async () => { |
| // Perform a normal search, to be able to clear the search box. |
| const appId = await testcase.searchDownloadsWithResults(); |
| |
| const clearButton = '#search-box .clear'; |
| // Wait for clear button. |
| await remoteCall.waitForElement(appId, clearButton); |
| |
| // Send a enter key to the clear button. |
| const enterKey = [clearButton, 'Enter', false, false, false]; |
| await remoteCall.fakeKeyDown(appId, ...enterKey); |
| |
| // Check: Search input field is empty. |
| const searchInput = |
| await remoteCall.waitForElement(appId, '#search-box [type="search"]'); |
| chrome.test.assertEq('', searchInput.value); |
| |
| // Wait until the search button get the focus. |
| // Use repeatUntil() here because the focus won't shift to search button |
| // until the CSS animation is finished. |
| const caller = getCaller(); |
| await repeatUntil(async () => { |
| const activeElement = |
| await remoteCall.callRemoteTestUtil('getActiveElement', appId, []); |
| if (activeElement.attributes['id'] !== 'search-button') { |
| return pending( |
| caller, 'Expected active element should be search-button, got %s', |
| activeElement.attributes['id']); |
| } |
| }); |
| }; |
| |
| /** |
| * Tests that the search text entry box stays expanded until the end of user |
| * interaction. |
| */ |
| testcase.searchHidingTextEntryField = async () => { |
| const entry = ENTRIES.hello; |
| |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS, [entry], []); |
| |
| // Select an entry in the file list. |
| await remoteCall.waitUntilSelected(appId, entry.nameText); |
| |
| // Click the toolbar search button. |
| await remoteCall.waitAndClickElement(appId, '#search-button'); |
| |
| // Verify the toolbar search text entry box is enabled. |
| let textInputElement = |
| await remoteCall.waitForElement(appId, ['#search-box cr-input', 'input']); |
| chrome.test.assertEq(undefined, textInputElement.attributes['disabled']); |
| |
| // Send a 'mousedown' to the toolbar 'delete' button. |
| chrome.test.assertTrue(await remoteCall.callRemoteTestUtil( |
| 'fakeEvent', appId, ['#delete-button', 'mousedown'])); |
| |
| // Verify the toolbar search text entry is still enabled. |
| textInputElement = |
| await remoteCall.waitForElement(appId, ['#search-box cr-input', 'input']); |
| chrome.test.assertEq(undefined, textInputElement.attributes['disabled']); |
| |
| // Send a 'mouseup' to the toolbar 'delete' button. |
| chrome.test.assertTrue(await remoteCall.callRemoteTestUtil( |
| 'fakeEvent', appId, ['#delete-button', 'mouseup'])); |
| |
| // Verify the toolbar search text entry is still enabled. |
| textInputElement = |
| await remoteCall.waitForElement(appId, ['#search-box cr-input', 'input']); |
| chrome.test.assertEq(undefined, textInputElement.attributes['disabled']); |
| }; |
| |
| /** |
| * Tests that the search box collapses when empty and Tab out of the box. |
| */ |
| testcase.searchHidingViaTab = async () => { |
| const entry = ENTRIES.hello; |
| |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS, [entry], []); |
| |
| // Search box should start collapsed. |
| await remoteCall.waitForElement(appId, '#search-wrapper[collapsed]'); |
| |
| // Click the toolbar search button. |
| await remoteCall.waitAndClickElement(appId, '#search-button'); |
| |
| // Wait for the search box to expand. |
| await remoteCall.waitForElementLost(appId, '#search-wrapper[collapsed]'); |
| |
| // Returns $i18n{} label if code coverage is enabled. |
| let expectedLabelText = 'Search'; |
| const isDevtoolsCoverageActive = |
| await sendTestMessage({name: 'isDevtoolsCoverageActive'}); |
| if (isDevtoolsCoverageActive === 'true') { |
| expectedLabelText = '$i18n{SEARCH_TEXT_LABEL}'; |
| } |
| |
| // Verify the search input has focus. |
| const input = |
| await remoteCall.callRemoteTestUtil('deepGetActiveElement', appId, []); |
| chrome.test.assertEq(input.attributes['id'], 'input'); |
| chrome.test.assertEq(input.attributes['aria-label'], expectedLabelText); |
| |
| // Send Tab key to focus the next element. |
| const result = await sendTestMessage({name: 'dispatchTabKey'}); |
| chrome.test.assertEq(result, 'tabKeyDispatched', 'Tab key dispatch failure'); |
| |
| // Check: the search box should collapse. |
| await remoteCall.waitForElement(appId, '#search-wrapper[collapsed]'); |
| }; |
| |
| /** |
| * Tests that clicking the search button expands and collapses the search box. |
| */ |
| testcase.searchButtonToggles = async () => { |
| const entry = ENTRIES.hello; |
| |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS, [entry], []); |
| |
| // Search box should start collapsed. |
| await remoteCall.waitForElement(appId, '#search-wrapper[collapsed]'); |
| |
| // Measure the width of the search box when it's collapsed. |
| const collapsedSearchBox = await remoteCall.waitForElementStyles( |
| appId, '#search-wrapper', ['width']); |
| |
| // Click the toolbar search button. |
| await remoteCall.waitAndClickElement(appId, '#search-button'); |
| |
| // Wait for the search box to expand. |
| await remoteCall.waitForElementLost(appId, '#search-wrapper[collapsed]'); |
| |
| // Check: The search box width should have increased. |
| const caller = getCaller(); |
| await repeatUntil(async () => { |
| const element = await remoteCall.waitForElementStyles( |
| appId, '#search-wrapper', ['width']); |
| if (collapsedSearchBox.renderedWidth >= element.renderedWidth) { |
| return pending(caller, 'Waiting search box to expand'); |
| } |
| }); |
| |
| // Click the toolbar search button again. |
| await remoteCall.waitAndClickElement(appId, '#search-button'); |
| |
| // Check: the search box should collapse. |
| await remoteCall.waitForElement(appId, '#search-wrapper[collapsed]'); |
| |
| // Check: the search box width should decrease. |
| await repeatUntil(async () => { |
| const element = await remoteCall.waitForElementStyles( |
| appId, '#search-wrapper', ['width']); |
| if (collapsedSearchBox.renderedWidth < element.renderedWidth) { |
| return pending(caller, 'Waiting search box to collapse'); |
| } |
| }); |
| }; |
| |
| /** |
| * Tests that Files app performs a search at app start up when |
| * LaunchParam.searchQuery is specified. |
| */ |
| testcase.searchQueryLaunchParam = async () => { |
| addEntries(['local'], BASIC_LOCAL_ENTRY_SET); |
| addEntries(['drive'], BASIC_DRIVE_ENTRY_SET); |
| |
| // Open Files app with LaunchParam.searchQuery='gdoc'. |
| const query = 'gdoc'; |
| /** @type {!FilesAppState} */ |
| const appState = {searchQuery: query}; |
| const appId = |
| await remoteCall.callRemoteTestUtil('openMainWindow', null, [appState]); |
| |
| // Check: search box should be filled with the query. |
| const caller = getCaller(); |
| await repeatUntil(async () => { |
| const searchBoxInput = |
| await remoteCall.waitForElement(appId, '#search-box cr-input'); |
| if (searchBoxInput.value !== query) { |
| return pending(caller, 'Waiting search box to be filled with the query.'); |
| } |
| }); |
| |
| // Check: "My Drive" directory should be selected because it is the sole |
| // directory that contains query-matched files (*.gdoc). |
| const selectedTreeRow = await remoteCall.waitForElement( |
| appId, '#directory-tree .tree-row[selected][active]'); |
| chrome.test.assertTrue(selectedTreeRow.text.includes('My Drive')); |
| |
| // Check: Query-matched files should be shown in the files list. |
| await repeatUntil(async () => { |
| const filenameLabel = |
| await remoteCall.waitForElement(appId, '#file-list .filename-label'); |
| if (!filenameLabel.text.includes(query)) { |
| // Pre-search results might be shown only for a moment before the search |
| // spinner is shown. |
| return pending(caller, 'Waiting files list to be updated.'); |
| } |
| }); |
| }; |
| |
| /** |
| * Checks that changing location options correctly filters search results. |
| */ |
| testcase.searchWithLocationOptions = async () => { |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Modify the basic entry set by adding nested directories and |
| // a copy of the hello entry. |
| const nestedHello = ENTRIES.hello.cloneWith({ |
| targetPath: 'A/hello.txt', |
| }); |
| addEntries(['local'], [ENTRIES.directoryA, nestedHello]); |
| |
| // Start in the nested directory, as the default search location |
| // is THIS_FOLDER. Expect to find one hello file. Then search on |
| // THIS_CHROMEBOOK and expect to find two. |
| await navigateWithDirectoryTree(appId, '/My files/Downloads/A'); |
| |
| // Search for all files with "hello" in their name. |
| await remoteCall.typeSearchText(appId, 'hello'); |
| |
| // Verify that the search options are visible. |
| await remoteCall.waitForElement(appId, 'xf-search-options:not([hidden])'); |
| |
| // Expect only the nested hello to be found. |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| nestedHello, |
| ])); |
| |
| // Click the second button, which is This Chromebook. |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'location', 2), |
| 'Failed to click "This Chromebook" location selector'); |
| |
| // Expect all hello files to be found. |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.hello, |
| nestedHello, |
| ])); |
| }; |
| |
| /** |
| * Checks that changing recency options correctly filters search results. |
| */ |
| testcase.searchWithRecencyOptions = async () => { |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Modify the basic entry set by adding another hello file with |
| // a recent date. We cannot make it today's date as those dates |
| // are rendered with 'Today' string rather than actual date string. |
| const recentHello = ENTRIES.hello.cloneWith({ |
| nameText: 'hello-recent.txt', |
| lastModifiedTime: new Date().toDateString(), |
| targetPath: 'hello-recent.txt', |
| }); |
| await addEntries(['local'], [recentHello]); |
| // Unfortunately, today's files use custom date string. Make it so. |
| const todayHello = recentHello.cloneWith({ |
| lastModifiedTime: 'Today 12:00 AM', |
| }); |
| |
| // Search for all files with "hello" in their name. |
| await remoteCall.typeSearchText(appId, 'hello'); |
| |
| // Expect two files, with no recency restrictions. |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.hello, |
| todayHello, |
| ])); |
| |
| // Click the fourth button, which is "Last week" option. |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'recency', 4), |
| 'Failed to click "Last week" recency selector'); |
| |
| // Expect only the recent hello file to be found. |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| todayHello, |
| ])); |
| }; |
| |
| /** |
| * Checks that when searching Google Drive we correctly match on name, not on |
| * contents. |
| */ |
| testcase.matchDriveFilesByName = async () => { |
| const appId = |
| await setupAndWaitUntilReady(RootPath.DRIVE, BASIC_LOCAL_ENTRY_SET, [ |
| ENTRIES.image2, |
| ]); |
| await remoteCall.typeSearchText(appId, 'image2'); |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([ENTRIES.image2])); |
| }; |
| |
| /** |
| * Checks that changing recency options correctly filters search results on |
| * drive. |
| */ |
| testcase.searchDriveWithRecencyOptions = async () => { |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Modify the basic entry set by adding another hello file with |
| // a recent date. We cannot make it today's date as those dates |
| // are rendered with 'Today' string rather than actual date string. |
| const recentHello = ENTRIES.hello.cloneWith({ |
| nameText: 'hello-recent.txt', |
| lastModifiedTime: getDateWithDayDiff(3), |
| targetPath: 'hello-recent.txt', |
| }); |
| await addEntries(['drive'], [recentHello]); |
| |
| // Navigate to Google Drive. We are searching "local" directory, which limits |
| // search results to Drive. |
| await navigateWithDirectoryTree(appId, '/My Drive'); |
| |
| // Search for all files with "hello" in their name. |
| await remoteCall.typeSearchText(appId, 'hello'); |
| |
| // Expect two files, with no recency restrictions. |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.hello, |
| recentHello, |
| ])); |
| |
| // Click the fourth button, which is "Last week" option. |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'recency', 4), |
| 'Failed to click "Last week" recency selector'); |
| |
| // Expect only the recent hello file to be found. |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| recentHello, |
| ])); |
| }; |
| |
| /** |
| * Checks that changing file types options correctly filters local |
| * search results. |
| */ |
| testcase.searchLocalWithTypeOptions = async () => { |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Search for all files with "hello" in their name. |
| await remoteCall.typeSearchText(appId, 'o'); |
| |
| // Expect all basic files, with no type restrictions. |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows(BASIC_LOCAL_ENTRY_SET)); |
| |
| // Click the fifth button, which is "Video" option. |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'type', 5), |
| 'Failed to click "Videos" type selector'); |
| |
| // Expect only world, which is a video file. |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.world, |
| ])); |
| }; |
| |
| /** |
| * Checks that changing file types options correctly filters |
| * Drive search results. |
| */ |
| testcase.searchDriveWithTypeOptions = async () => { |
| // Open Files app on Downloads. |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Navigate to Google Drive; make sure we have the desired files. |
| await navigateWithDirectoryTree(appId, '/My Drive'); |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows(BASIC_DRIVE_ENTRY_SET)); |
| |
| // Search the Drive for all files with "b" in their name. |
| await remoteCall.typeSearchText(appId, 'b'); |
| |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.desktop, |
| ENTRIES.beautiful, |
| ])); |
| |
| // Click the second button, which is "Audio" option. |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'type', 2), |
| 'Failed to click "Audio" type selector'); |
| |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.beautiful, |
| ])); |
| }; |
| |
| /** |
| * @param {boolean} withPartitions Whether or not USB has partitions. |
| * @return {string} The label that can be used to query for elements. |
| */ |
| function getUsbVolumeQuery(withPartitions) { |
| return `#directory-tree [entry-label=${ |
| withPartitions ? '"Drive Label"' : '"fake-usb"'}]`; |
| } |
| |
| /** |
| * @param {string} appId The ID of the files app under test. |
| * @param {boolean} withPartitions Whether or not USB has partitions. |
| */ |
| async function mountUsb(appId, withPartitions) { |
| const nameSuffix = withPartitions ? 'UsbWithPartitions' : 'FakeUsb'; |
| await sendTestMessage({name: `mount${nameSuffix}`}); |
| await remoteCall.waitForElement(appId, getUsbVolumeQuery(withPartitions)); |
| } |
| |
| /** |
| * Checks that the new search correctly finds files on a USB drive. |
| */ |
| testcase.searchRemovableDevice = async () => { |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| // Mount a USB with no partitions. |
| await mountUsb(appId, false); |
| |
| // Navigate to the root of the USB. |
| await remoteCall.callRemoteTestUtil( |
| 'fakeMouseClick', appId, [getUsbVolumeQuery(false)]); |
| |
| await remoteCall.typeSearchText(appId, 'hello'); |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.hello, |
| ]), |
| {ignoreLastModifiedTime: true}); |
| }; |
| |
| /** |
| * Checks that the new search correctly finds files on a USB drive with multiple |
| * partitions. |
| */ |
| testcase.searchPartitionedRemovableDevice = async () => { |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| await mountUsb(appId, true); |
| |
| // Wait for removable partition-1 to appear in the directory tree. |
| const partitionOne = await remoteCall.waitForElement( |
| appId, '#directory-tree [entry-label="partition-1"]'); |
| chrome.test.assertEq( |
| 'removable', partitionOne.attributes['volume-type-for-testing']); |
| |
| // Wait for removable partition-2 to appear in the directory tree. |
| const partitionTwo = await remoteCall.waitForElement( |
| appId, '#directory-tree [entry-label="partition-2"]'); |
| chrome.test.assertEq( |
| 'removable', partitionTwo.attributes['volume-type-for-testing']); |
| |
| // Navigate to the root of the USB. |
| await remoteCall.callRemoteTestUtil( |
| 'fakeMouseClick', appId, [getUsbVolumeQuery(true)]); |
| |
| // Search for the 'hello' and expect two files; ignore the modified time |
| // as these were copied when mounting the USB. |
| await remoteCall.typeSearchText(appId, 'hello'); |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.hello, |
| ENTRIES.hello, |
| ]), |
| {ignoreLastModifiedTime: true}); |
| }; |
| /** |
| * Checks that the search options are reset to default on folder change. |
| */ |
| testcase.resetSearchOptionsOnFolderChange = async () => { |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Type something into the search query to see search options. |
| await remoteCall.typeSearchText(appId, 'b'); |
| |
| // Check the defaults. |
| chrome.test.assertEq( |
| 'Downloads', await getSelectedOptionText(appId, 'location')); |
| chrome.test.assertEq( |
| 'Any time', await getSelectedOptionText(appId, 'recency')); |
| chrome.test.assertEq('All types', await getSelectedOptionText(appId, 'type')); |
| |
| // Change options. |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'type', 2), |
| 'Failed to change to "Audio" type selector'); |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'recency', 4), |
| 'Failed to change to "Last week" recency selector'); |
| |
| await navigateWithDirectoryTree(appId, '/My files/Downloads/photos'); |
| |
| // Start search again. |
| await remoteCall.typeSearchText(appId, 'b'); |
| |
| // Check that we are back to defaults. |
| chrome.test.assertEq( |
| 'photos', await getSelectedOptionText(appId, 'location')); |
| chrome.test.assertEq( |
| 'Any time', await getSelectedOptionText(appId, 'recency')); |
| chrome.test.assertEq('All types', await getSelectedOptionText(appId, 'type')); |
| }; |
| |
| /** |
| * Checks that we are showing the correct message in breadcrumbs when search is |
| * active. |
| */ |
| testcase.showSearchResultMessageWhenSearching = async () => { |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| |
| // Check that we start with My Files |
| const beforeSearchPath = |
| await remoteCall.callRemoteTestUtil('getBreadcrumbPath', appId, []); |
| chrome.test.assertEq('/My files/Downloads', beforeSearchPath); |
| |
| // Type something into the search query to start search. |
| await remoteCall.typeSearchText(appId, 'b'); |
| |
| // Wait for the search to fully expand. |
| await remoteCall.waitForElementLost(appId, '#search-wrapper[collapsed]'); |
| |
| // Check that the breadcumb shows that we are searching. |
| const duringSearchPath = |
| await remoteCall.callRemoteTestUtil('getBreadcrumbPath', appId, []); |
| chrome.test.assertEq('/Search results', duringSearchPath); |
| |
| // Clear and close search. |
| await remoteCall.waitAndClickElement(appId, '#search-box .clear'); |
| |
| // Wait for the search to fully close. |
| await remoteCall.waitForElement(appId, '#search-wrapper[collapsed]'); |
| |
| // Expect the path to return to the original path. |
| const afterSearchPath = |
| await remoteCall.callRemoteTestUtil('getBreadcrumbPath', appId, []); |
| chrome.test.assertEq(beforeSearchPath, afterSearchPath); |
| }; |
| |
| /** |
| * Tests that the educational nudge is displayed when the files |
| * app is started with V2 version of search enabled. |
| */ |
| testcase.showsEducationNudge = async () => { |
| // Open the Files app. |
| const appId = await setupAndWaitUntilReady(RootPath.DRIVE); |
| |
| // Check that the nudge and its text is visible. |
| await remoteCall.waitNudge(appId, 'New search features available'); |
| }; |
| |
| /** |
| * Checks that search works correctly when starting in My Files. |
| */ |
| testcase.searchFromMyFiles = async () => { |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| await navigateWithDirectoryTree(appId, '/My files'); |
| const beforeSearchPath = |
| await remoteCall.callRemoteTestUtil('getBreadcrumbPath', appId, []); |
| chrome.test.assertEq('/My files', beforeSearchPath); |
| |
| // Make sure the search returns a matching file even when originating in My |
| // Files rather than My Files/Downloads directory. |
| await remoteCall.typeSearchText(appId, 'hello'); |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.hello, |
| ])); |
| |
| // Close the search before adding Linux files. |
| await remoteCall.waitAndClickElement(appId, '#search-box .clear'); |
| await remoteCall.waitForElement(appId, '#search-wrapper[collapsed]'); |
| |
| // Add Linux files. |
| await mountCrostini(appId); |
| // Add some Linux specific files. |
| await addEntries(['crostini'], [ENTRIES.debPackage]); |
| // Navigate back to /My files |
| await navigateWithDirectoryTree(appId, '/My files'); |
| |
| // Search for files containing ack (should include debPackage. |
| await remoteCall.typeSearchText(appId, 'ack'); |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.desktop, |
| ENTRIES.desktop, |
| ENTRIES.debPackage, |
| ])); |
| }; |
| |
| /** |
| * Checks that the selection path correctly reflects paths of elements found by |
| * search. |
| */ |
| testcase.selectionPath = async () => { |
| const appId = |
| await setupAndWaitUntilReady(RootPath.DOWNLOADS, NESTED_ENTRY_SET.concat([ |
| ENTRIES.hello, |
| ENTRIES.desktop, |
| ENTRIES.deeplyBurriedSmallJpeg, |
| ])); |
| // Search for files containing 'e'; should be three of those. |
| await remoteCall.typeSearchText(appId, 'e'); |
| await remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows([ |
| ENTRIES.hello, |
| ENTRIES.desktop, |
| ENTRIES.deeplyBurriedSmallJpeg, |
| ])); |
| await remoteCall.waitUntilSelected(appId, ENTRIES.hello.nameText); |
| const singleSelectionPath = await remoteCall.waitForElement(appId, [ |
| 'xf-path-display', |
| ]); |
| chrome.test.assertFalse(singleSelectionPath.hidden); |
| chrome.test.assertEq( |
| 'My files/Downloads/' + ENTRIES.hello.nameText, |
| singleSelectionPath.attributes.path); |
| // Select now the desktop entry, too. Two or more selected files, |
| // regardless of the directory in which they sit, result in no path. |
| await remoteCall.waitAndClickElement( |
| appId, `#file-list [file-name="${ENTRIES.desktop.nameText}"]`, |
| {ctrl: true}); |
| const twoFilesSelectedPath = await remoteCall.waitForElement(appId, [ |
| 'xf-path-display', |
| ]); |
| chrome.test.assertTrue(twoFilesSelectedPath.hidden); |
| chrome.test.assertEq('', twoFilesSelectedPath.attributes.path); |
| await remoteCall.waitAndClickElement( |
| appId, |
| `#file-list [file-name="${ENTRIES.deeplyBurriedSmallJpeg.nameText}"]`, |
| {ctrl: true}); |
| const threeFilesSelectedPath = await remoteCall.waitForElement(appId, [ |
| 'xf-path-display', |
| ]); |
| chrome.test.assertTrue(threeFilesSelectedPath.hidden); |
| chrome.test.assertEq('', threeFilesSelectedPath.attributes.path); |
| |
| // Close search. Select any file. Confirm that the path display is not shown, |
| // now that the search is inactive. |
| await remoteCall.waitAndClickElement(appId, '#search-box .clear'); |
| await remoteCall.waitUntilSelected(appId, ENTRIES.hello.nameText); |
| const pathDisplayWhileNotSearching = await remoteCall.waitForElement(appId, [ |
| 'xf-path-display', |
| ]); |
| chrome.test.assertTrue(pathDisplayWhileNotSearching.hidden); |
| }; |
| |
| /** |
| * Checks that we correctly traverse search hierarchy. If you start searching in |
| * a local folder, the search should search it and its subfolders only. If we |
| * change the location to be the root directory, it should correctly search any |
| * folders (including Linux and Playfiles, if necessary) that are visually |
| * under the root folder. Finally, search everywhere should search everything |
| * we can search (Google Doc, removable drives, local file syste, * etc.). |
| */ |
| testcase.searchHierarchy = async () => { |
| // hello file stored in My files/Downloads/photos. |
| const photosHello = ENTRIES.hello.cloneWith({ |
| targetPath: 'photos/photos-hello.txt', |
| nameText: 'photos-hello.txt', |
| }); |
| // hello file stored in My files |
| const myFilesHello = ENTRIES.hello.cloneWith({ |
| targetPath: 'my-files-hello.txt', |
| nameText: 'my-files-hello.txt', |
| }); |
| // hello file stored on Linux. |
| const linuxHello = ENTRIES.hello.cloneWith({ |
| targetPath: 'linux-hello.txt', |
| nameText: 'linux-hello.txt', |
| }); |
| // hello file stored on a removable drive. |
| const usbHello = ENTRIES.hello.cloneWith({ |
| targetPath: 'usb-hello.txt', |
| nameText: 'usb-hello.txt', |
| }); |
| // hello file stored on Google Drive. |
| const driveHello = ENTRIES.hello.cloneWith({ |
| targetPath: 'drive-hello.txt', |
| nameText: 'drive-hello.txt', |
| }); |
| // hello file stored in playfiles. |
| const playfilesHello = ENTRIES.hello.cloneWith({ |
| targetPath: 'Documents/playfile-hello.txt', |
| nameText: 'playfile-hello.txt', |
| }); |
| |
| // Set up the app. This creates entries in My files and Drive. |
| const appId = await setupAndWaitUntilReady( |
| RootPath.DOWNLOADS, [myFilesHello, ENTRIES.photos, photosHello], |
| [driveHello]); |
| |
| // Mount USBs. |
| await mountUsb(appId, false); |
| |
| // Add Linux files. |
| await mountCrostini(appId); |
| |
| // Add custom hello files to Linux, USB, and PlayFiles. |
| await addEntries(['android_files'], BASIC_ANDROID_ENTRY_SET.concat([ |
| ENTRIES.directoryDocuments, |
| playfilesHello, |
| ])); |
| await addEntries(['usb'], [usbHello]); |
| await addEntries(['crostini'], [linuxHello]); |
| |
| // Move to a nested directory under My files. |
| await navigateWithDirectoryTree(appId, '/My files/Downloads/photos'); |
| |
| // Expect photosHello, as the only result when searching in photos. |
| await remoteCall.typeSearchText(appId, '-hello.txt'); |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([photosHello]), |
| {ignoreLastModifiedTime: true}); |
| |
| // Select the second button, which is root directory (My Fles in our case). |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'location', 2), |
| 'Failed to click "My files" location selector'); |
| |
| // Expect files from My files, Play files and Linux files. |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([ |
| myFilesHello, |
| photosHello, |
| linuxHello, |
| playfilesHello, |
| ]), |
| {ignoreLastModifiedTime: true}); |
| |
| // Select the first button, which is Everywhere. |
| chrome.test.assertTrue( |
| !!await remoteCall.selectSearchOption(appId, 'location', 1), |
| 'Failed to click "Everywhere" location selector'); |
| |
| // Expect files from My files, Play files, Linux files, USB, and Drive. |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([ |
| myFilesHello, |
| photosHello, |
| linuxHello, |
| playfilesHello, |
| driveHello, |
| usbHello, |
| ]), |
| {ignoreLastModifiedTime: true}); |
| }; |
| |
| /** |
| * Checks that search is not visible when in the Trash volume. |
| */ |
| testcase.hideSearchInTrash = async () => { |
| const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS); |
| // Make sure that the search button is visible in Downloads. |
| await remoteCall.waitForElement(appId, '#search-button'); |
| let searchButton = await remoteCall.waitForElementStyles( |
| appId, ['#search-button'], ['display']); |
| chrome.test.assertTrue(searchButton.styles['display'] !== 'none'); |
| |
| // Navigate to Trash and confirm that the search button is now hidden. |
| await navigateWithDirectoryTree(appId, '/Trash'); |
| searchButton = await remoteCall.waitForElementStyles( |
| appId, ['#search-button'], ['display']); |
| chrome.test.assertTrue(searchButton.styles['display'] === 'none'); |
| |
| // Try to use keyboard shortcuts Ctrl+F to launch search anyway. |
| const ctrlF = ['#file-list', 'f', true, false, false]; |
| await remoteCall.fakeKeyDown(appId, ...ctrlF); |
| |
| const searchWrapper = |
| await remoteCall.waitForElement(appId, ['#search-wrapper']); |
| // Confirm that search wrapper is still in collapsed state. |
| chrome.test.assertEq(searchWrapper.attributes.collapsed, ''); |
| |
| // Go back to Downloads and confirm that the search button is visible again. |
| await navigateWithDirectoryTree(appId, '/My files/Downloads'); |
| searchButton = await remoteCall.waitForElementStyles( |
| appId, ['#search-button'], ['display']); |
| chrome.test.assertTrue(searchButton.styles['display'] !== 'none'); |
| |
| // Make sure that search still works. |
| await remoteCall.typeSearchText(appId, 'hello'); |
| await remoteCall.waitForFiles( |
| appId, TestEntryInfo.getExpectedRows([ENTRIES.hello])); |
| }; |