Commit 2b7f29d7 authored by Alex Lau's avatar Alex Lau Committed by Commit Bot

Add mock ARC volume and initial tests.

This adds a fake local ARC volume for testing. Initial integration
tests added check that
1) the "Toggle Play Folders" button work in hiding and showing the
   non-default Android files and folders.
2) the "Toggle Play Folders" button is shown inside Play Files but
   hidden from the "Recents" pane.

Bug: 877371
Test: Tested on Chromium build.
Change-Id: Ic35de5ebb06f901ad229c9b35f1917ac9353a459
Reviewed-on: https://chromium-review.googlesource.com/1201637
Commit-Queue: Alex Lau <alexlau@chromium.org>
Reviewed-by: default avatarNaoki Fukino <fukino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589847}
parent f57f5f8e
......@@ -565,14 +565,17 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
WRAPPED_INSTANTIATE_TEST_CASE_P(
GearMenu, /* gear_menu.js */
FilesAppBrowserTest,
::testing::Values(TestCase("showHiddenFilesDownloads"),
::testing::Values(
TestCase("showHiddenFilesDownloads"),
TestCase("showHiddenFilesDownloads").InGuestMode(),
TestCase("showHiddenFilesDrive"),
TestCase("showHiddenFilesDrive").EnableDriveFs(),
TestCase("toogleGoogleDocsDrive"),
TestCase("toogleGoogleDocsDrive").EnableDriveFs(),
TestCase("showPasteIntoCurrentFolder"),
TestCase("showSelectAllInCurrentFolder")));
TestCase("showSelectAllInCurrentFolder"),
TestCase("showToggleHiddenAndroidFoldersGearMenuItemsInMyFiles"),
TestCase("enableToggleHiddenAndroidFoldersShowsHiddenFiles")));
WRAPPED_INSTANTIATE_TEST_CASE_P(
Crostini, /* crostini.js */
......
......@@ -100,7 +100,13 @@ struct AddEntriesMessage {
struct TestEntryInfo;
// Represents the various volumes available for adding entries.
enum TargetVolume { LOCAL_VOLUME, DRIVE_VOLUME, CROSTINI_VOLUME, USB_VOLUME };
enum TargetVolume {
LOCAL_VOLUME,
DRIVE_VOLUME,
CROSTINI_VOLUME,
USB_VOLUME,
ANDROID_FILES_VOLUME
};
// Represents the different types of entries (e.g. file, folder).
enum EntryType { FILE, DIRECTORY, TEAM_DRIVE };
......@@ -143,6 +149,8 @@ struct AddEntriesMessage {
*volume = CROSTINI_VOLUME;
else if (value == "usb")
*volume = USB_VOLUME;
else if (value == "android_files")
*volume = ANDROID_FILES_VOLUME;
else
return false;
return true;
......@@ -488,6 +496,30 @@ class DownloadsTestVolume : public LocalTestVolume {
DISALLOW_COPY_AND_ASSIGN(DownloadsTestVolume);
};
class AndroidFilesTestVolume : public LocalTestVolume {
public:
AndroidFilesTestVolume() : LocalTestVolume("AndroidFiles") {}
~AndroidFilesTestVolume() override = default;
bool Initialize(Profile* profile) { return CreateRootDirectory(profile); }
bool Mount(Profile* profile) override {
return CreateRootDirectory(profile) &&
VolumeManager::Get(profile)->RegisterAndroidFilesDirectoryForTesting(
root_path());
}
const base::FilePath& mount_path() const { return root_path(); }
void Unmount(Profile* profile) {
VolumeManager::Get(profile)->RemoveAndroidFilesDirectoryForTesting(
root_path());
}
private:
DISALLOW_COPY_AND_ASSIGN(AndroidFilesTestVolume);
};
// CrostiniTestVolume: local test volume for the "Linux files" directory.
class CrostiniTestVolume : public LocalTestVolume {
public:
......@@ -1040,6 +1072,9 @@ void FileManagerBrowserTestBase::SetUpOnMainThread() {
->AddCustomMountPointCallback(
base::BindRepeating(&FileManagerBrowserTestBase::MaybeMountCrostini,
base::Unretained(this)));
android_files_volume_ = std::make_unique<AndroidFilesTestVolume>();
android_files_volume_->Mount(profile());
}
display_service_ =
......@@ -1173,6 +1208,10 @@ void FileManagerBrowserTestBase::OnCommand(const std::string& name,
dictionary.SetString(
"drive", base::StrCat({"/", drive_mount_name.value(), "/root"}));
}
if (android_files_volume_) {
dictionary.SetString("android_files",
"/" + util::GetAndroidFilesMountPointName());
}
}
base::JSONWriter::Write(dictionary, output);
return;
......@@ -1229,6 +1268,11 @@ void FileManagerBrowserTestBase::OnCommand(const std::string& name,
LOG(FATAL) << "Add entry: but no USB volume.";
}
break;
case AddEntriesMessage::ANDROID_FILES_VOLUME:
CHECK(android_files_volume_);
ASSERT_TRUE(android_files_volume_->Initialize(profile()));
android_files_volume_->CreateEntry(*message.entries[i]);
break;
}
}
......
......@@ -25,6 +25,7 @@ class DriveTestVolume;
class FakeTestVolume;
class LocalTestVolume;
class CrostiniTestVolume;
class AndroidFilesTestVolume;
class FileManagerBrowserTestBase : public extensions::ExtensionApiTest {
protected:
......@@ -109,6 +110,7 @@ class FileManagerBrowserTestBase : public extensions::ExtensionApiTest {
std::unique_ptr<LocalTestVolume> local_volume_;
std::unique_ptr<CrostiniTestVolume> crostini_volume_;
std::unique_ptr<AndroidFilesTestVolume> android_files_volume_;
std::map<Profile*, std::unique_ptr<DriveTestVolume>> drive_volumes_;
DriveTestVolume* drive_volume_ = nullptr;
std::unique_ptr<FakeTestVolume> usb_volume_;
......
......@@ -37,6 +37,7 @@ const char kDownloadsFolderName[] = "Downloads";
const char kGoogleDriveDisplayName[] = "Google Drive";
const char kRootRelativeToDriveMount[] = "root";
const char kTeamDrivesRelativeToDriveMount[] = "team_drives";
const char kAndroidFilesMountPointName[] = "android_files";
// Sync with the file provider in ARC++ side.
constexpr char kArcFileProviderUrl[] =
......@@ -128,6 +129,10 @@ std::string GetDownloadsMountPointName(Profile* profile) {
return net::EscapeQueryParamValue(kDownloadsFolderName + id, false);
}
std::string GetAndroidFilesMountPointName() {
return kAndroidFilesMountPointName;
}
std::string GetCrostiniMountPointName(Profile* profile) {
// crostini_<hash>_termina_penguin
return base::JoinString(
......
......@@ -46,6 +46,9 @@ bool MigratePathFromOldFormat(Profile* profile,
// The canonical mount point name for "Downloads" folder.
std::string GetDownloadsMountPointName(Profile* profile);
// The canonical mount point name for ARC "Play files" folder.
std::string GetAndroidFilesMountPointName();
// The canonical mount point name for crostini "Linux files" folder.
std::string GetCrostiniMountPointName(Profile* profile);
......
......@@ -54,7 +54,6 @@ const uint32_t kFilesystemTypeGenericHierarchical = 2;
const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
const char kMtpVolumeIdPrefix[] = "mtp:";
const char kRootPath[] = "/";
const char kAndroidFilesMountPointName[] = "android_files";
// Registers |path| as the "Downloads" folder to the FileSystem API backend.
// If another folder is already mounted. It revokes and overrides the old one.
......@@ -89,8 +88,8 @@ bool RegisterAndroidFilesMountPoint() {
storage::ExternalMountPoints* const mount_points =
storage::ExternalMountPoints::GetSystemInstance();
return mount_points->RegisterFileSystem(
kAndroidFilesMountPointName, storage::kFileSystemTypeNativeLocal,
storage::FileSystemMountOption(),
file_manager::util::GetAndroidFilesMountPointName(),
storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
base::FilePath(util::kAndroidFilesPath));
}
......@@ -342,13 +341,14 @@ std::unique_ptr<Volume> Volume::CreateForSshfsCrostini(
}
// static
std::unique_ptr<Volume> Volume::CreateForAndroidFiles() {
std::unique_ptr<Volume> Volume::CreateForAndroidFiles(
const base::FilePath& mount_path) {
std::unique_ptr<Volume> volume(new Volume());
volume->type_ = VOLUME_TYPE_ANDROID_FILES;
volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
// Keep source_path empty.
volume->source_ = SOURCE_SYSTEM;
volume->mount_path_ = base::FilePath(util::kAndroidFilesPath);
volume->mount_path_ = mount_path;
volume->mount_condition_ = chromeos::disks::MOUNT_CONDITION_NONE;
volume->volume_id_ = GenerateVolumeId(*volume);
volume->watchable_ = true;
......@@ -570,6 +570,27 @@ void VolumeManager::RemoveSshfsCrostiniVolume(
chromeos::disks::DiskMountManager::UnmountPathCallback());
}
bool VolumeManager::RegisterAndroidFilesDirectoryForTesting(
const base::FilePath& path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
bool result =
storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
file_manager::util::GetAndroidFilesMountPointName(),
storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
path);
DCHECK(result);
DoMountEvent(chromeos::MOUNT_ERROR_NONE, Volume::CreateForAndroidFiles(path));
return true;
}
bool VolumeManager::RemoveAndroidFilesDirectoryForTesting(
const base::FilePath& path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
*Volume::CreateForAndroidFiles(path));
return true;
}
bool VolumeManager::RegisterDownloadsDirectoryForTesting(
const base::FilePath& path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
......@@ -931,7 +952,9 @@ void VolumeManager::OnArcPlayStoreEnabledChanged(bool enabled) {
DoMountEvent(chromeos::MOUNT_ERROR_NONE,
Volume::CreateForMediaView(arc::kAudioRootDocumentId));
if (IsShowAndroidFilesEnabled())
DoMountEvent(chromeos::MOUNT_ERROR_NONE, Volume::CreateForAndroidFiles());
DoMountEvent(chromeos::MOUNT_ERROR_NONE,
Volume::CreateForAndroidFiles(
base::FilePath(util::kAndroidFilesPath)));
} else {
DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
*Volume::CreateForMediaView(arc::kImagesRootDocumentId));
......@@ -941,7 +964,8 @@ void VolumeManager::OnArcPlayStoreEnabledChanged(bool enabled) {
*Volume::CreateForMediaView(arc::kAudioRootDocumentId));
if (IsShowAndroidFilesEnabled()) {
DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
*Volume::CreateForAndroidFiles());
*Volume::CreateForAndroidFiles(
base::FilePath(util::kAndroidFilesPath)));
}
}
......
......@@ -106,7 +106,8 @@ class Volume : public base::SupportsWeakPtr<Volume> {
const std::string& root_document_id);
static std::unique_ptr<Volume> CreateForSshfsCrostini(
const base::FilePath& crostini_path);
static std::unique_ptr<Volume> CreateForAndroidFiles();
static std::unique_ptr<Volume> CreateForAndroidFiles(
const base::FilePath& mount_path);
static std::unique_ptr<Volume> CreateForTesting(
const base::FilePath& path,
VolumeType volume_type,
......@@ -304,6 +305,14 @@ class VolumeManager : public KeyedService,
// |path| with CROSTINI type, and adds its volume info.
bool RegisterCrostiniDirectoryForTesting(const base::FilePath& path);
// For testing purpose, registers a native local file system pointing to
// |path| with ANDROID_FILES type, and adds its volume info.
bool RegisterAndroidFilesDirectoryForTesting(const base::FilePath& path);
// For testing purpose, removes a registered native local file system
// pointing to |path| with ANDROID_FILES type, and removes its volume info.
bool RemoveAndroidFilesDirectoryForTesting(const base::FilePath& path);
// For testing purpose, adds a volume info pointing to |path|, with TESTING
// type. Assumes that the mount point is already registered.
void AddVolumeForTesting(const base::FilePath& path,
......
......@@ -457,6 +457,7 @@ window.addEventListener('load', function() {
var roots = JSON.parse(paths);
RootPath.DOWNLOADS = roots.downloads;
RootPath.DRIVE = roots.drive;
RootPath.ANDROID_FILES = roots.android_files;
chrome.test.sendMessage(
JSON.stringify({name: 'getTestName'}), steps.shift());
},
......
......@@ -35,6 +35,16 @@ const BASIC_DRIVE_ENTRY_SET_WITH_HIDDEN = [
ENTRIES.hiddenFile
];
const BASIC_ANDROID_ENTRY_SET = [
ENTRIES.directoryDocuments, ENTRIES.directoryMovies, ENTRIES.directoryMusic,
ENTRIES.directoryPictures
];
const BASIC_ANDROID_ENTRY_SET_WITH_HIDDEN = [
ENTRIES.directoryDocuments, ENTRIES.directoryMovies, ENTRIES.directoryMusic,
ENTRIES.directoryPictures, ENTRIES.hello, ENTRIES.world, ENTRIES.directoryA
];
/**
* Expected files shown in Drive with Google Docs disabled
*
......@@ -57,11 +67,39 @@ const BASIC_DRIVE_ENTRY_SET_WITHOUT_GDOCS = [
* @return {!Array} The test steps to toggle hidden files
*/
function getTestCaseStepsForHiddenFiles(basicSet, hiddenEntrySet) {
return getTestCaseStepsForHiddenFilesWithMenuItem(
basicSet, hiddenEntrySet, '#gear-menu-toggle-hidden-files');
}
/**
* Gets the common steps to toggle Android hidden files in the Files app
* @param {!Array<!TestEntryInfo>} basicSet Files expected before showing hidden
* @param {!Array<!TestEntryInfo>} hiddenEntrySet Files expected after showing
* hidden
* @return {!Array} The test steps to toggle hidden files
*/
function getTestCaseStepsForAndroidHiddenFiles(basicSet, hiddenEntrySet) {
return getTestCaseStepsForHiddenFilesWithMenuItem(
basicSet, hiddenEntrySet, '#gear-menu-toggle-hidden-android-folders');
}
/**
* Gets the common steps to toggle hidden files in the Files app
* @param {!Array<!TestEntryInfo>} basicSet Files expected before showing hidden
* @param {!Array<!TestEntryInfo>} hiddenEntrySet Files expected after showing
* hidden
* @param {string} toggleMenuItemSelector Selector for the menu item that
* toggles hidden file visibility
* @return {!Array} The test steps to toggle hidden files
*/
function getTestCaseStepsForHiddenFilesWithMenuItem(
basicSet, hiddenEntrySet, toggleMenuItemSelector) {
var appId;
return [
function(id) {
appId = id;
remoteCall.waitForElement(appId, '#gear-button').then(this.next);
remoteCall.waitForElement(appId, '#gear-button:not([hidden])')
.then(this.next);
},
// Open the gear menu by clicking the gear button.
function() {
......@@ -76,25 +114,26 @@ function getTestCaseStepsForHiddenFiles(basicSet, hiddenEntrySet) {
},
// Wait for menu item to appear.
function(result) {
remoteCall.waitForElement(appId,
'#gear-menu-toggle-hidden-files:not([disabled])').then(this.next);
remoteCall
.waitForElement(appId, toggleMenuItemSelector + ':not([disabled])')
.then(this.next);
},
// Wait for menu item to appear.
function(result) {
remoteCall.waitForElement(appId,
'#gear-menu-toggle-hidden-files:not([checked])').then(this.next);
remoteCall
.waitForElement(appId, toggleMenuItemSelector + ':not([checked])')
.then(this.next);
},
// Click the menu item.
function(results) {
remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['#gear-menu-toggle-hidden-files'],
this.next);
'fakeMouseClick', appId, [toggleMenuItemSelector], this.next);
},
// Wait for item to be checked.
function(result) {
chrome.test.assertTrue(result);
remoteCall.waitForElement(appId,
'#gear-menu-toggle-hidden-files[checked]').then(this.next);
remoteCall.waitForElement(appId, toggleMenuItemSelector + '[checked]')
.then(this.next);
},
// Check the hidden files are displayed.
function(result) {
......@@ -104,7 +143,8 @@ function getTestCaseStepsForHiddenFiles(basicSet, hiddenEntrySet) {
},
// Repeat steps to toggle again.
function(inAppId) {
remoteCall.waitForElement(appId, '#gear-button').then(this.next);
remoteCall.waitForElement(appId, '#gear-button:not([hidden])')
.then(this.next);
},
function() {
remoteCall.callRemoteTestUtil(
......@@ -116,22 +156,23 @@ function getTestCaseStepsForHiddenFiles(basicSet, hiddenEntrySet) {
this.next);
},
function(result) {
remoteCall.waitForElement(appId,
'#gear-menu-toggle-hidden-files:not([disabled])').then(this.next);
remoteCall
.waitForElement(appId, toggleMenuItemSelector + ':not([disabled])')
.then(this.next);
},
function(result) {
remoteCall.waitForElement(appId,
'#gear-menu-toggle-hidden-files[checked]').then(this.next);
remoteCall.waitForElement(appId, toggleMenuItemSelector + '[checked]')
.then(this.next);
},
function(results) {
remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['#gear-menu-toggle-hidden-files'],
this.next);
'fakeMouseClick', appId, [toggleMenuItemSelector], this.next);
},
function(result) {
chrome.test.assertTrue(result);
remoteCall.waitForElement(appId,
'#gear-menu-toggle-hidden-files:not([checked])').then(this.next);
remoteCall
.waitForElement(appId, toggleMenuItemSelector + ':not([checked])')
.then(this.next);
},
function(result) {
remoteCall.waitForFiles(appId, TestEntryInfo.getExpectedRows(basicSet),
......@@ -276,6 +317,126 @@ testcase.toogleGoogleDocsDrive = function() {
]);
};
/**
* Tests that toggle-hidden-android-folders menu item exists when "Play files"
* is selected, but hidden in Recents.
*/
testcase.showToggleHiddenAndroidFoldersGearMenuItemsInMyFiles = function() {
var appId;
StepsRunner.run([
// Open Files.App on Play Files.
function() {
openNewWindow(null, RootPath.ANDROID_FILES).then(this.next);
},
function(inAppId) {
appId = inAppId;
addEntries(['android_files'], BASIC_ANDROID_ENTRY_SET, this.next);
},
// Wait for the file list to appear.
function(result) {
chrome.test.assertTrue(result);
remoteCall.waitForElement(appId, '#file-list').then(this.next);
},
// Wait for the gear menu button to appear.
function() {
remoteCall.waitForElement(appId, '#gear-button:not([hidden])')
.then(this.next);
},
// Click the gear menu button.
function() {
remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['#gear-button'], this.next);
},
// Wait for the gear menu to appear.
function(result) {
chrome.test.assertTrue(result);
remoteCall.waitForElement(appId, '#gear-menu:not([hidden])')
.then(this.next);
},
// #toggle-hidden-android-folders command should be shown and disabled by
// default.
function() {
remoteCall
.waitForElement(
appId,
'#gear-menu-toggle-hidden-android-folders' +
':not([checked]):not([hidden])')
.then(this.next);
},
// Click the file list: the gear menu should hide.
function() {
remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['#file-list'], this.next);
},
// Wait for the gear menu to hide.
function() {
remoteCall.waitForElement(appId, '#gear-menu[hidden]').then(this.next);
},
// Navigate to Recent.
function() {
remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['span[root-type-icon=\'recent\']'],
this.next);
},
// Click the gear menu button.
function() {
remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['#gear-button'], this.next);
},
// Wait for the gear menu to appear.
function(result) {
chrome.test.assertTrue(result);
remoteCall.waitForElement(appId, '#gear-menu:not([hidden])')
.then(this.next);
},
// #toggle-hidden-android-folders command should be hidden.
function(result) {
remoteCall
.waitForElement(
appId, '#gear-menu-toggle-hidden-android-folders[hidden]')
.then(this.next);
},
function() {
checkIfNoErrorsOccured(this.next);
},
]);
};
/**
* Tests that "Play files" shows the full set of files after
* toggle-hidden-android-folders is enabled.
*/
testcase.enableToggleHiddenAndroidFoldersShowsHiddenFiles = function() {
var appId;
var steps = [
// Open Files.App on Play Files.
function() {
openNewWindow(null, RootPath.ANDROID_FILES).then(this.next);
},
function(inAppId) {
appId = inAppId;
addEntries(
['android_files'], BASIC_ANDROID_ENTRY_SET_WITH_HIDDEN, this.next);
},
// Wait for the file list to appear.
function(result) {
chrome.test.assertTrue(result);
remoteCall.waitForElement(appId, '#file-list').then(this.next);
},
// Wait for the gear menu button to appear.
function() {
remoteCall.waitForElement(appId, '#gear-button:not([hidden])')
.then(this.next);
},
function() {
this.next(appId);
}
];
steps = steps.concat(getTestCaseStepsForAndroidHiddenFiles(
BASIC_ANDROID_ENTRY_SET, BASIC_ANDROID_ENTRY_SET_WITH_HIDDEN));
StepsRunner.run(steps);
};
/**
* Tests the paste-into-current-folder menu item.
*/
......
......@@ -12,6 +12,7 @@ testcase.showMyFiles = function() {
'Recent: FakeItem',
'My files: EntryListItem',
'Downloads: SubDirectoryItem',
'Play files: SubDirectoryItem',
'Linux files: FakeItem',
'Google Drive: DriveVolumeItem',
'My Drive: SubDirectoryItem',
......@@ -192,10 +193,11 @@ testcase.myFilesDisplaysAndOpensEntries = function() {
function(result) {
chrome.test.assertTrue(result);
const downloadsRow = ['Downloads', '--', 'Folder'];
const playFilesRow = ['Play files', '--', 'Folder'];
const crostiniRow = ['Linux files', '--', 'Folder'];
remoteCall
.waitForFiles(
appId, [downloadsRow, crostiniRow],
appId, [downloadsRow, playFilesRow, crostiniRow],
{ignoreFileSize: true, ignoreLastModifiedTime: true})
.then(this.next);
},
......
......@@ -218,6 +218,7 @@ var SharedOption = Object.freeze({
var RootPath = Object.seal({
DOWNLOADS: '/must-be-filled-in-test-setup',
DRIVE: '/must-be-filled-in-test-setup',
ANDROID_FILES: '/must-be-filled-in-test-setup',
});
......@@ -732,5 +733,67 @@ var ENTRIES = {
canDelete: false,
canShare: true
},
})
}),
// Default Android directories.
directoryDocuments: new TestEntryInfo({
type: EntryType.DIRECTORY,
targetPath: 'Documents',
lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
nameText: 'Documents',
sizeText: '--',
typeText: 'Folder',
capabilities: {
canCopy: false,
canAddChildren: true,
canRename: false,
canDelete: false,
canShare: true
},
}),
directoryMovies: new TestEntryInfo({
type: EntryType.DIRECTORY,
targetPath: 'Movies',
lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
nameText: 'Movies',
sizeText: '--',
typeText: 'Folder',
capabilities: {
canCopy: false,
canAddChildren: true,
canRename: false,
canDelete: false,
canShare: true
},
}),
directoryMusic: new TestEntryInfo({
type: EntryType.DIRECTORY,
targetPath: 'Music',
lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
nameText: 'Music',
sizeText: '--',
typeText: 'Folder',
capabilities: {
canCopy: false,
canAddChildren: true,
canRename: false,
canDelete: false,
canShare: true
},
}),
directoryPictures: new TestEntryInfo({
type: EntryType.DIRECTORY,
targetPath: 'Pictures',
lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
nameText: 'Pictures',
sizeText: '--',
typeText: 'Folder',
capabilities: {
canCopy: false,
canAddChildren: true,
canRename: false,
canDelete: false,
canShare: true
},
}),
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment