chromium/ui/file_manager/file_manager/state/ducks/folder_shortcuts_unittest.ts

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {MockFileSystem} from '../../common/js/mock_entry.js';
import type {State} from '../../state/state.js';
import {convertEntryToFileData} from '../ducks/all_entries.js';
import {setUpFileManagerOnWindow, setupStore, waitDeepEquals} from '../for_tests.js';
import {getEmptyState} from '../store.js';

import {addFolderShortcut, refreshFolderShortcut, removeFolderShortcut} from './folder_shortcuts.js';

export function setUp() {
  setUpFileManagerOnWindow();
}

/** Generate a fake file system with fake file entries. */
function setupFileSystem(): MockFileSystem {
  const fileSystem = new MockFileSystem('fake-fs');
  fileSystem.populate([
    '/shortcut-1/',
    '/shortcut-2/',
    '/shortcut-3/',
    '/shortcut-4/',
  ]);
  return fileSystem;
}

/** Tests folder shortcuts can be refreshed correctly. */
export async function testRefreshFolderShortcuts(done: () => void) {
  const initialState = getEmptyState();
  // Add shortcut-1 to the store.
  const fileSystem = setupFileSystem();
  const shortcutEntry1 = fileSystem.entries['/shortcut-1'] as DirectoryEntry;
  initialState.allEntries[shortcutEntry1.toURL()] =
      convertEntryToFileData(shortcutEntry1);
  initialState.folderShortcuts.push(shortcutEntry1.toURL());

  const store = setupStore(initialState);

  // Dispatch a refresh action with shortcut 2 and 3.
  const shortcutEntry2 = fileSystem.entries['/shortcut-2'] as DirectoryEntry;
  const shortcutEntry3 = fileSystem.entries['/shortcut-3'] as DirectoryEntry;
  store.dispatch(
      refreshFolderShortcut({entries: [shortcutEntry2, shortcutEntry3]}));

  // Expect all shortcut entries are in allEntries, and only shortcut 2 and 3
  // are in the folderShortcuts.
  const want: Partial<State> = {
    allEntries: {
      [shortcutEntry2.toURL()]: convertEntryToFileData(shortcutEntry2),
      [shortcutEntry3.toURL()]: convertEntryToFileData(shortcutEntry3),
    },
    folderShortcuts: [shortcutEntry2.toURL(), shortcutEntry3.toURL()],
  };
  await waitDeepEquals(store, want, (state) => ({
                                      allEntries: state.allEntries,
                                      folderShortcuts: state.folderShortcuts,
                                    }));

  done();
}

/** Tests folder shortcut can be added correctly. */
export async function testAddFolderShortcut(done: () => void) {
  const initialState = getEmptyState();
  // Add shortcut-1 and shortcut-3 to the store.
  const fileSystem = setupFileSystem();
  const shortcutEntry1 = fileSystem.entries['/shortcut-1'] as DirectoryEntry;
  const shortcutEntry3 = fileSystem.entries['/shortcut-3'] as DirectoryEntry;
  initialState.allEntries[shortcutEntry1.toURL()] =
      convertEntryToFileData(shortcutEntry1);
  initialState.allEntries[shortcutEntry3.toURL()] =
      convertEntryToFileData(shortcutEntry3);
  initialState.folderShortcuts.push(shortcutEntry1.toURL());
  initialState.folderShortcuts.push(shortcutEntry3.toURL());

  const store = setupStore(initialState);

  // Dispatch an action to add shortcut 4.
  const shortcutEntry4 = fileSystem.entries['/shortcut-4'] as DirectoryEntry;
  store.dispatch(addFolderShortcut({entry: shortcutEntry4}));

  // Expect the newly added shortcut 4 is in the store.
  const want1: Partial<State> = {
    allEntries: {
      [shortcutEntry1.toURL()]: convertEntryToFileData(shortcutEntry1),
      [shortcutEntry3.toURL()]: convertEntryToFileData(shortcutEntry3),
      [shortcutEntry4.toURL()]: convertEntryToFileData(shortcutEntry4),
    },
    folderShortcuts: [
      shortcutEntry1.toURL(),
      shortcutEntry3.toURL(),
      shortcutEntry4.toURL(),
    ],
  };
  await waitDeepEquals(store, want1, (state) => ({
                                       allEntries: state.allEntries,
                                       folderShortcuts: state.folderShortcuts,
                                     }));

  // Dispatch another action to add already existed shortcut 1.
  store.dispatch(addFolderShortcut({entry: shortcutEntry1}));

  // Expect no changes in the store.
  await waitDeepEquals(store, want1, (state) => ({
                                       allEntries: state.allEntries,
                                       folderShortcuts: state.folderShortcuts,
                                     }));

  // Dispatch another action to add shortcut 2 to check sorting.
  const shortcutEntry2 = fileSystem.entries['/shortcut-2'] as DirectoryEntry;
  store.dispatch(addFolderShortcut({entry: shortcutEntry2}));

  // Expect shortcut 2 will be inserted in the middle.
  const want2: Partial<State> = {
    allEntries: {
      ...want1.allEntries,
      [shortcutEntry2.toURL()]: convertEntryToFileData(shortcutEntry2),
    },
    folderShortcuts: [
      shortcutEntry1.toURL(),
      shortcutEntry2.toURL(),
      shortcutEntry3.toURL(),
      shortcutEntry4.toURL(),
    ],
  };
  await waitDeepEquals(store, want2, (state) => ({
                                       allEntries: state.allEntries,
                                       folderShortcuts: state.folderShortcuts,
                                     }));

  done();
}

/** Tests folder shortcut can be removed correctly. */
export async function testRemoveFolderShortcut(done: () => void) {
  const initialState = getEmptyState();
  // Add shortcut-1 to the store.
  const fileSystem = setupFileSystem();
  const shortcutEntry1 = fileSystem.entries['/shortcut-1'] as DirectoryEntry;
  initialState.allEntries[shortcutEntry1.toURL()] =
      convertEntryToFileData(shortcutEntry1);
  initialState.folderShortcuts.push(shortcutEntry1.toURL());

  const store = setupStore(initialState);

  // Dispatch an action to remove shortcut 1.
  store.dispatch(removeFolderShortcut({key: shortcutEntry1.toURL()}));

  // Expect shortcut 1 is removed from the store.
  await waitDeepEquals(store, [], (state) => state.folderShortcuts);

  // Dispatch another action to remove non-existed shortcut 2.
  const shortcutEntry2 = fileSystem.entries['/shortcut-2'] as DirectoryEntry;
  store.dispatch(removeFolderShortcut({key: shortcutEntry2.toURL()}));

  // Expect no changes in the store.
  await waitDeepEquals(store, [], (state) => state.folderShortcuts);

  done();
}