Skip to content

Commit

Permalink
Reland "[Files F2] Use want/got style in unit test to do assertion"
Browse files Browse the repository at this point in the history
This is a reland of CL:4318782 because its parent CL has been
reverted. No additional changes.

* Remove some mock methods because now we use the real data to convert
  entry to FileData, or to convert volumeInfo/volumeMetadata to Volume.
* Add missing ui entries to the store:
  * MyFiles entry list
  * Google Drive entry list
  * Grouped removable entry list

Bug: b:271485133
Test: browser_tests --gtest_filter="*FileManagerJsTest*Reducer*"
Change-Id: I11b8332ab8503fe4cfa15bdfc8e7b445a35cd968
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4337085
Reviewed-by: Luciano Pacheco <lucmult@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1117315}
  • Loading branch information
PinkyJie committed Mar 15, 2023
1 parent 87c8390 commit 1aca512
Show file tree
Hide file tree
Showing 9 changed files with 1,155 additions and 1,141 deletions.
Expand Up @@ -120,7 +120,7 @@ export class MockVolumeManager {
* Current implementation can handle only fake entries.
*
* @param {!Entry|!FilesAppEntry} entry A fake entry.
* @return {!EntryLocation} Location information.
* @return {!EntryLocation|null} Location information.
*/
getLocationInfo(entry) {
if (util.isFakeEntry(entry)) {
Expand Down Expand Up @@ -160,6 +160,11 @@ export class MockVolumeManager {
}

const volumeInfo = this.getVolumeInfo(entry);
// For filtered out volumes, its volume info won't exist in the volume info
// list.
if (!volumeInfo) {
return null;
}
const rootType = VolumeManagerCommon.getRootTypeFromVolumeType(
assert(volumeInfo.volumeType));
const isRootEntry = util.isSameEntry(entry, volumeInfo.fileSystem.root);
Expand Down
134 changes: 44 additions & 90 deletions ui/file_manager/file_manager/state/for_tests.ts
Expand Up @@ -6,13 +6,11 @@ import {assertDeepEquals} from 'chrome://webui-test/chromeos/chai_assert.js';

import {MockVolumeManager} from '../background/js/mock_volume_manager.js';
import {DialogType} from '../common/js/dialog_type.js';
import {VolumeManagerCommon} from '../common/js/volume_manager_types.js';
import {Crostini} from '../externs/background/crostini.js';
import {FilesAppDirEntry} from '../externs/files_app_entry_interfaces.js';
import {FileData, FileKey, PropStatus, State, Volume} from '../externs/ts/state.js';
import {constants} from '../foreground/js/constants.js';
import {FileKey, PropStatus, State} from '../externs/ts/state.js';
import {VolumeInfo} from '../externs/volume_info.js';
import {FileSelectionHandler} from '../foreground/js/file_selection.js';
import {MetadataItem} from '../foreground/js/metadata/metadata_item.js';
import {MetadataModel} from '../foreground/js/metadata/metadata_model.js';
import {MockMetadataModel} from '../foreground/js/metadata/mock_metadata.js';
import {createFakeDirectoryModel} from '../foreground/js/mock_directory_model.js';
Expand Down Expand Up @@ -76,6 +74,21 @@ export function updateContent(store: Store, entries: Entry[]) {
store.dispatch(updateDirectoryContent({entries}));
}

/**
* Store state might include objects (e.g. Entry type) which can not stringified
* by JSON, here we implement a custom "replacer" to handle that.
*/
function jsonStringifyStoreState(state: any): string {
return JSON.stringify(state, (key, value) => {
// Currently only the key with "entry" (inside `FileData`) can't be
// stringified, we just return its URL.
if (key === 'entry') {
return value.toURL();
}
return value;
}, 2);
}

/**
* Waits for a part of the Store to be in the expected state.
*
Expand All @@ -92,7 +105,9 @@ export async function waitDeepEquals(
let got: any;
const timeout = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`waitDeepEquals timed out waiting for \n${want}`));
reject(new Error(`waitDeepEquals timed out.\nWANT:\n${
jsonStringifyStoreState(
want)}\nGOT:\n${jsonStringifyStoreState(got)}`));
}, 10000);
});

Expand All @@ -105,8 +120,6 @@ export async function waitDeepEquals(
if (error.constructor?.name === 'AssertionError') {
return false;
}
console.log(error.stack);
console.error(error);
throw error;
}
});
Expand All @@ -115,9 +128,9 @@ export async function waitDeepEquals(
}

/** Setup store and initialize it with empty state. */
export function setupStore(): Store {
export function setupStore(initialState: State = getEmptyState()): Store {
const store = getStore();
store.init(getEmptyState());
store.init(initialState);
return store;
}

Expand All @@ -139,98 +152,39 @@ export function setUpFileManagerOnWindow() {
}

/**
* Create a fake FileData with partial information. Only the fields listed are
* required, other fields are optional.
* Create a fake VolumeMetadata with VolumeInfo, VolumeInfo can be created by
* MockVolumeManager.createMockVolumeInfo.
*/
export function createFakeFileData(
partialFileData: Pick<FileData, 'entry'|'label'|'type'>&
Partial<Omit<FileData, 'entry'|'label'|'type'>>,
): FileData {
const defaultFileData = {
icon: constants.ICON_TYPES.FOLDER,
volumeType: null,
isDirectory: true,
metadata: {} as MetadataItem,
isRootEntry: false,
isEjectable: false,
shouldDelayLoadingChildren: false,
children: [],
expanded: false,
};
return {
...defaultFileData,
...partialFileData,
};
}

/** Create a fake VolumeMetadata. */
export function createFakeVolumeMetadata(
partialMetadata:
Pick<chrome.fileManagerPrivate.VolumeMetadata, 'volumeId'|'volumeType'>&
Partial<Omit<
chrome.fileManagerPrivate.VolumeMetadata, 'volumeId'|'volumeType'>>,
volumeInfo: VolumeInfo,
): chrome.fileManagerPrivate.VolumeMetadata {
const defaultMetadata = {
return {
volumeId: volumeInfo.volumeId,
volumeType: volumeInfo.volumeType,
profile: {
displayName: 'foobar@chromium.org',
isCurrentProfile: true,
...volumeInfo.profile,
profileId: '',
},
configurable: false,
watchable: true,
source: VolumeManagerCommon.Source.SYSTEM,
volumeLabel: undefined,
configurable: volumeInfo.configurable,
watchable: volumeInfo.watchable,
source: volumeInfo.source,
volumeLabel: volumeInfo.label,
fileSystemId: undefined,
providerId: undefined,
providerId: volumeInfo.providerId,
sourcePath: undefined,
deviceType: undefined,
devicePath: undefined,
deviceType: volumeInfo.deviceType,
devicePath: volumeInfo.devicePath,
isParentDevice: undefined,
isReadOnly: false,
isReadOnlyRemovableDevice: false,
hasMedia: false,
isReadOnly: volumeInfo.isReadOnly,
isReadOnlyRemovableDevice: volumeInfo.isReadOnlyRemovableDevice,
hasMedia: volumeInfo.hasMedia,
mountCondition: undefined,
mountContext: undefined,
diskFileSystemType: undefined,
iconSet: {icon16x16Url: '', icon32x32Url: ''},
driveLabel: '',
remoteMountPath: undefined,
diskFileSystemType: volumeInfo.diskFileSystemType,
iconSet: volumeInfo.iconSet,
driveLabel: volumeInfo.driveLabel,
remoteMountPath: volumeInfo.remoteMountPath,
hidden: false,
vmType: undefined,
};
return {
...defaultMetadata,
...partialMetadata,
};
}

/**
* Create a fake Volume. Only the fields listed are required, other fields are
* optional.
*/
export function createFakeVolume(
partialVolume: Pick<Volume, 'volumeId'|'volumeType'|'rootKey'|'label'>&
Partial<Omit<Volume, 'volumeId'|'volumeType'|'rootKey'|'label'>>): Volume {
const defaultVolume = {
status: PropStatus.SUCCESS,
source: VolumeManagerCommon.Source.SYSTEM,
error: undefined,
deviceType: undefined,
devicePath: undefined,
isReadOnly: false,
isReadOnlyRemovableDevice: false,
providerId: undefined,
configurable: false,
watchable: true,
diskFileSystemType: '',
iconSet: {icon16x16Url: '', icon32x32Url: ''},
driveLabel: '',
vmType: undefined,
isDisabled: false,
prefixKey: undefined,
};
return {
...defaultVolume,
...partialVolume,
vmType: volumeInfo.vmType,
};
}
14 changes: 12 additions & 2 deletions ui/file_manager/file_manager/state/reducers/all_entries.ts
Expand Up @@ -360,6 +360,7 @@ function getEntryType(entry: Entry|FilesAppEntry): EntryType {
case VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT:
return EntryType.ENTRY_LIST;
case VolumeManagerCommon.RootType.CROSTINI:
case VolumeManagerCommon.RootType.ANDROID_FILES:
return EntryType.PLACEHOLDER;
case VolumeManagerCommon.RootType.DRIVE_OFFLINE:
case VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME:
Expand Down Expand Up @@ -436,6 +437,7 @@ export function getMyFiles(state: State):
myFilesEntryList = new EntryList(
str('MY_FILES_ROOT_LABEL'), VolumeManagerCommon.RootType.MY_FILES);
appendEntry(state, myFilesEntryList);
state.uiEntries = [...state.uiEntries, myFilesEntryList.toURL()];
}

return {
Expand Down Expand Up @@ -484,6 +486,9 @@ function volumeNestingEntries(
// Also remove it from the children field.
myFilesFileData.children = myFilesFileData.children.filter(
childKey => childKey !== childEntry.toURL());
// And remove it from the uiEntries if existed.
state.uiEntries = state.uiEntries.filter(
uiEntryKey => uiEntryKey !== childEntry.toURL());
}
}
appendChildIfNotExisted(myFilesEntry, newVolumeEntry);
Expand Down Expand Up @@ -513,6 +518,9 @@ function volumeNestingEntries(
appendChildIfNotExisted(myFilesVolumeEntry!, childEntry);
myFilesEntryList.removeChildEntry(childEntry);
}
// Remove MyFiles entry list from the uiEntries.
state.uiEntries = state.uiEntries.filter(
uiEntryKey => uiEntryKey !== myFilesEntryListKey);
}
}

Expand All @@ -527,6 +535,7 @@ function volumeNestingEntries(
str('DRIVE_DIRECTORY_LABEL'),
VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT);
appendEntry(state, googleDrive);
state.uiEntries = [...state.uiEntries, googleDrive.toURL()];
}
appendChildIfNotExisted(googleDrive, myDrive!);

Expand Down Expand Up @@ -559,15 +568,15 @@ function volumeNestingEntries(
fakeEntries[VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME];
if (fakeSharedWithMe) {
appendEntry(state, fakeSharedWithMe);
state.uiEntries.push(fakeSharedWithMe.toURL());
state.uiEntries = [...state.uiEntries, fakeSharedWithMe.toURL()];
appendChildIfNotExisted(googleDrive, fakeSharedWithMe);
}

// Add "Offline" into Drive.
const fakeOffline = fakeEntries[VolumeManagerCommon.RootType.DRIVE_OFFLINE];
if (fakeOffline) {
appendEntry(state, fakeOffline);
state.uiEntries.push(fakeOffline.toURL());
state.uiEntries = [...state.uiEntries, fakeOffline.toURL()];
appendChildIfNotExisted(googleDrive, fakeOffline);
}
}
Expand All @@ -591,6 +600,7 @@ function volumeNestingEntries(
volumeMetadata.driveLabel || '',
VolumeManagerCommon.RootType.REMOVABLE, volumeMetadata.devicePath);
appendEntry(state, parentEntry);
state.uiEntries = [...state.uiEntries, parentEntry.toURL()];
// Removable devices with group, its parent should always be ejectable.
state.allEntries[parentKey].isEjectable = true;
}
Expand Down

0 comments on commit 1aca512

Please sign in to comment.