chromium/chrome/test/data/webui/chromeos/settings/os_files_page/files_settings_card_test.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 'chrome://os-settings/lazy_load.js';

import {FilesSettingsCardElement, OneDriveConnectionState, SmbBrowserProxyImpl} from 'chrome://os-settings/lazy_load.js';
import {createRouterForTesting, CrLinkRowElement, CrSettingsPrefs, OneDriveBrowserProxy, Route, Router, routes, SettingsPrefsElement} from 'chrome://os-settings/os_settings.js';
import {assert} from 'chrome://resources/js/assert.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';

import {OneDriveTestBrowserProxy, ProxyOptions} from './one_drive_test_browser_proxy.js';
import {TestSmbBrowserProxy} from './test_smb_browser_proxy.js';

suite('<files-settings-card>', () => {
  const isRevampWayfindingEnabled =
      loadTimeData.getBoolean('isRevampWayfindingEnabled');
  const route =
      isRevampWayfindingEnabled ? routes.SYSTEM_PREFERENCES : routes.FILES;

  let filesSettingsCard: FilesSettingsCardElement;
  let prefElement: SettingsPrefsElement;
  let smbBrowserProxy: TestSmbBrowserProxy;

  /**
   * Returns a list of fake preferences that are used at some point in any test,
   * if another element is added that requires a new pref ensure to add it here.
   */
  function getFakePrefs() {
    return [
      {
        key: 'gdata.disabled',
        type: chrome.settingsPrivate.PrefType.BOOLEAN,
        value: false,
      },
      {
        key: 'gdata.cellular.disabled',
        type: chrome.settingsPrivate.PrefType.BOOLEAN,
        value: false,
      },
      {
        key: 'drivefs.bulk_pinning_enabled',
        type: chrome.settingsPrivate.PrefType.BOOLEAN,
        value: false,
      },
      {
        key: 'drivefs.enable_mirror_sync',
        type: chrome.settingsPrivate.PrefType.BOOLEAN,
        value: false,
      },
      // The OneDrive preferences that are required when navigating to the
      // officeFiles page route.
      {
        key: 'filebrowser.office.always_move_to_onedrive',
        type: chrome.settingsPrivate.PrefType.BOOLEAN,
        value: false,
      },
      {
        key: 'filebrowser.office.always_move_to_drive',
        type: chrome.settingsPrivate.PrefType.BOOLEAN,
        value: false,
      },
      {
        key: 'network_file_shares.allowed.value',
        type: chrome.settingsPrivate.PrefType.BOOLEAN,
        value: true,
      },
    ];
  }

  async function createFilesSettingsCard() {
    prefElement = document.createElement('settings-prefs');
    const fakeSettingsPrivate = new FakeSettingsPrivate(getFakePrefs());
    prefElement.initialize(fakeSettingsPrivate);
    await CrSettingsPrefs.initialized;

    filesSettingsCard = document.createElement('files-settings-card');
    filesSettingsCard.prefs = prefElement.prefs;
    document.body.appendChild(filesSettingsCard);
    await flushTasks();
  }

  function getGoogleDriveRowSubLabel(): HTMLElement {
    const subLabel =
        filesSettingsCard.shadowRoot!.getElementById('googleDriveSubLabel');
    assert(subLabel);
    return subLabel;
  }

  async function assertSubpageTriggerFocused(
      triggerSelector: string, route: Route): Promise<void> {
    const subpageTrigger =
        filesSettingsCard.shadowRoot!.querySelector<HTMLElement>(
            triggerSelector);
    assert(subpageTrigger);

    // Subpage trigger navigates to subpage for route
    subpageTrigger.click();
    assertEquals(route, Router.getInstance().currentRoute);

    // Navigate back
    const popStateEventPromise = eventToPromise('popstate', window);
    Router.getInstance().navigateToPreviousRoute();
    await popStateEventPromise;
    await waitAfterNextRender(filesSettingsCard);

    assertEquals(
        subpageTrigger, filesSettingsCard.shadowRoot!.activeElement,
        `${triggerSelector} should be focused.`);
  }

  suiteSetup(() => {
    smbBrowserProxy = new TestSmbBrowserProxy();
    SmbBrowserProxyImpl.setInstance(smbBrowserProxy);

    CrSettingsPrefs.deferInitialization = true;
  });

  setup(() => {
    loadTimeData.overrideValues({
      showOneDriveSettings: false,
      showOfficeSettings: false,
      enableDriveFsBulkPinning: false,
    });

    Router.getInstance().navigateTo(route);
  });

  teardown(() => {
    filesSettingsCard.remove();
    prefElement.remove();
    smbBrowserProxy.reset();
    Router.getInstance().resetRouteForTesting();
  });

  test(
      'OneDrive row is not stamped when showOfficeSettings is false',
      async () => {
        await createFilesSettingsCard();
        assertNull(filesSettingsCard.shadowRoot!.querySelector('#oneDriveRow'));
      });

  test(
      'Office row is not stamped when showOfficeSettings is false',
      async () => {
        await createFilesSettingsCard();
        assertNull(filesSettingsCard.shadowRoot!.querySelector('#officeRow'));
      });

  test('Google Drive row sublabel changes based on pref value', async () => {
    await createFilesSettingsCard();
    filesSettingsCard.setPrefValue('gdata.disabled', true);
    flush();

    const googleDriveRowSubLabel = getGoogleDriveRowSubLabel();
    assertEquals('Not signed in', googleDriveRowSubLabel.innerText);

    filesSettingsCard.setPrefValue('gdata.disabled', false);
    flush();
    assertTrue(googleDriveRowSubLabel.innerText.startsWith('Signed in as'));
  });

  test('Google Drive row is focused when returning from subpage', async () => {
    await createFilesSettingsCard();

    await assertSubpageTriggerFocused('#googleDriveRow', routes.GOOGLE_DRIVE);
  });

  suite('with showOneDriveSettings set to true', () => {
    let testOneDriveBrowserProxy: OneDriveTestBrowserProxy;

    function setupBrowserProxy(options: ProxyOptions): void {
      testOneDriveBrowserProxy = new OneDriveTestBrowserProxy(options);
      OneDriveBrowserProxy.setInstance(testOneDriveBrowserProxy);
    }

    setup(() => {
      loadTimeData.overrideValues({showOneDriveSettings: true});

      // Reinitialize Router and routes based on load time data
      const testRouter = createRouterForTesting();
      Router.resetInstanceForTesting(testRouter);
    });

    teardown(() => {
      testOneDriveBrowserProxy.handler.reset();
    });

    test('OneDrive row shows disconnected when no email set up', async () => {
      setupBrowserProxy({email: null});
      await createFilesSettingsCard();

      const oneDriveRow =
          filesSettingsCard.shadowRoot!.querySelector<CrLinkRowElement>(
              '#oneDriveRow');
      assert(oneDriveRow);
      assertEquals('Add your Microsoft account', oneDriveRow.subLabel);
    });

    test('OneDrive row shows email address', async () => {
      const email = '[email protected]';
      setupBrowserProxy({email});
      await createFilesSettingsCard();

      const oneDriveRow =
          filesSettingsCard.shadowRoot!.querySelector<CrLinkRowElement>(
              '#oneDriveRow');
      assert(oneDriveRow);
      assertEquals(`Signed in as ${email}`, oneDriveRow.subLabel);
    });

    test('OneDrive row shows loading state', async () => {
      const email = '[email protected]';
      setupBrowserProxy({email});
      await createFilesSettingsCard();

      const oneDriveRow =
          filesSettingsCard.shadowRoot!.querySelector<CrLinkRowElement>(
              '#oneDriveRow');
      assert(oneDriveRow);

      // Change connection status to "LOADING".
      filesSettingsCard.updateOneDriveConnectionStateForTesting(
          OneDriveConnectionState.LOADING);
      flush();
      assertEquals('Loading…', oneDriveRow.subLabel);
    });

    test('OneDrive row shows email address on OneDrive mount', async () => {
      setupBrowserProxy({email: null});
      await createFilesSettingsCard();

      const oneDriveRow =
          filesSettingsCard.shadowRoot!.querySelector<CrLinkRowElement>(
              '#oneDriveRow');
      assert(oneDriveRow);
      assertEquals('Add your Microsoft account', oneDriveRow.subLabel);

      // Simulate OneDrive mount: mount signal to observer and ability to return
      // an email address.
      const email = '[email protected]';
      testOneDriveBrowserProxy.handler.setResultFor(
          'getUserEmailAddress', {email});
      testOneDriveBrowserProxy.observerRemote.onODFSMountOrUnmount();
      await flushTasks();
      assertEquals(`Signed in as ${email}`, oneDriveRow.subLabel);
    });

    test('OneDrive row removes email address on OneDrive unmount', async () => {
      const email = '[email protected]';
      setupBrowserProxy({email});
      await createFilesSettingsCard();

      const oneDriveRow =
          filesSettingsCard.shadowRoot!.querySelector<CrLinkRowElement>(
              '#oneDriveRow');
      assert(oneDriveRow);
      assertEquals(`Signed in as ${email}`, oneDriveRow.subLabel);

      // Simulate OneDrive unmount: unmount signal and returns an empty email
      // address.
      testOneDriveBrowserProxy.handler.setResultFor(
          'getUserEmailAddress', {email: null});
      testOneDriveBrowserProxy.observerRemote.onODFSMountOrUnmount();
      await flushTasks();
      assertEquals('Add your Microsoft account', oneDriveRow.subLabel);
    });

    test('OneDrive row is focused when returning from subpage', async () => {
      setupBrowserProxy({email: null});
      await createFilesSettingsCard();

      await assertSubpageTriggerFocused('#oneDriveRow', routes.ONE_DRIVE);
    });
  });

  suite('with showOfficeSettings set to true', () => {
    let testOneDriveBrowserProxy: OneDriveTestBrowserProxy;

    function setupBrowserProxy(options: ProxyOptions): void {
      testOneDriveBrowserProxy = new OneDriveTestBrowserProxy(options);
      OneDriveBrowserProxy.setInstance(testOneDriveBrowserProxy);
    }

    setup(() => {
      loadTimeData.overrideValues({showOfficeSettings: true});

      // Reinitialize Router and routes based on load time data
      const testRouter = createRouterForTesting();
      Router.resetInstanceForTesting(testRouter);
    });

    teardown(() => {
      testOneDriveBrowserProxy.handler.reset();
    });

    test('Clicking office row navigates to office route', async () => {
      setupBrowserProxy({email: null});
      await createFilesSettingsCard();

      const officeRow =
          filesSettingsCard.shadowRoot!.querySelector<HTMLElement>(
              '#officeRow');
      assert(officeRow);

      officeRow.click();
      flush();
      assertEquals(routes.OFFICE, Router.getInstance().currentRoute);
    });

    test('Office row is focused when returning from subpage', async () => {
      setupBrowserProxy({email: null});
      await createFilesSettingsCard();

      await assertSubpageTriggerFocused('#officeRow', routes.OFFICE);
    });
  });

  suite('with enableDriveFsBulkPinning set to true', () => {
    setup(async () => {
      loadTimeData.overrideValues({
        enableDriveFsBulkPinning: true,
      });
    });

    test(
        'with gdata.disabled set to true, text shows appropriately',
        async () => {
          await createFilesSettingsCard();
          filesSettingsCard.setPrefValue('gdata.disabled', true);
          flush();
          assertEquals('Not signed in', getGoogleDriveRowSubLabel().innerText);
        });

    test(
        'with gdata.disabled set to false, but file sync disabled',
        async () => {
          await createFilesSettingsCard();
          filesSettingsCard.setPrefValue('drivefs.bulk_pinning_enabled', false);
          flush();

          assertTrue(
              getGoogleDriveRowSubLabel().innerText.startsWith('Signed in as'));
        });

    test(
        'with gdata.disabled set to false, and file sync enabled', async () => {
          await createFilesSettingsCard();
          filesSettingsCard.setPrefValue('drivefs.bulk_pinning_enabled', true);
          flush();

          assertEquals('File sync on', getGoogleDriveRowSubLabel().innerText);
        });

    test('cycling through the prefs updates the sublabel texts', async () => {
      await createFilesSettingsCard();
      filesSettingsCard.setPrefValue('gdata.disabled', true);
      filesSettingsCard.setPrefValue('drivefs.bulk_pinning_enabled', false);
      flush();

      const googleDriveRowSubLabel = getGoogleDriveRowSubLabel();
      assertEquals('Not signed in', googleDriveRowSubLabel.innerText);

      filesSettingsCard.setPrefValue('gdata.disabled', false);
      flush();
      assertTrue(googleDriveRowSubLabel.innerText.startsWith('Signed in as'));

      filesSettingsCard.setPrefValue('drivefs.bulk_pinning_enabled', true);
      flush();
      assertEquals('File sync on', googleDriveRowSubLabel.innerText);
    });
  });

  suite('with enableDriveFsMirrorSync set to true', () => {
    setup(async () => {
      loadTimeData.overrideValues({
        enableDriveFsMirrorSync: true,
      });
    });

    test(
        'with gdata.disabled set to true, text shows appropriately',
        async () => {
          await createFilesSettingsCard();
          filesSettingsCard.setPrefValue('gdata.disabled', true);
          flush();
          assertEquals('Not signed in', getGoogleDriveRowSubLabel().innerText);
        });

    test(
        'with gdata.disabled set to false, but mirror sync disabled',
        async () => {
          await createFilesSettingsCard();
          filesSettingsCard.setPrefValue('drivefs.enable_mirror_sync', false);
          flush();

          assertTrue(
              getGoogleDriveRowSubLabel().innerText.startsWith('Signed in as'));
        });

    test(
        'with gdata.disabled set to false, and mirror sync enabled',
        async () => {
          await createFilesSettingsCard();
          filesSettingsCard.setPrefValue('drivefs.enable_mirror_sync', true);
          flush();

          assertEquals('File sync on', getGoogleDriveRowSubLabel().innerText);
        });

    test('cycling through the prefs updates the sublabel texts', async () => {
      await createFilesSettingsCard();
      filesSettingsCard.setPrefValue('gdata.disabled', true);
      filesSettingsCard.setPrefValue('drivefs.enable_mirror_sync', false);
      flush();

      const googleDriveRowSubLabel = getGoogleDriveRowSubLabel();
      assertEquals('Not signed in', googleDriveRowSubLabel.innerText);

      filesSettingsCard.setPrefValue('gdata.disabled', false);
      flush();
      assertTrue(googleDriveRowSubLabel.innerText.startsWith('Signed in as'));

      filesSettingsCard.setPrefValue('drivefs.enable_mirror_sync', true);
      flush();
      assertEquals('File sync on', googleDriveRowSubLabel.innerText);
    });
  });

  if (isRevampWayfindingEnabled) {
    suite('when no share has been setup before', () => {
      setup(async () => {
        smbBrowserProxy.anySmbMounted = false;
      });

      test('File shares row is not visible', async () => {
        await createFilesSettingsCard();
        await smbBrowserProxy.whenCalled('hasAnySmbMountedBefore');

        const smbSharesLinkRow =
            filesSettingsCard.shadowRoot!.querySelector('#smbSharesRow');
        assertFalse(isVisible(smbSharesLinkRow));
      });

      test('Add file shares row is visible', async () => {
        await createFilesSettingsCard();
        await smbBrowserProxy.whenCalled('hasAnySmbMountedBefore');

        const addSmbSharesRow =
            filesSettingsCard.shadowRoot!.querySelector('#addSmbSharesRow');
        assertTrue(isVisible(addSmbSharesRow));
      });
    });

    suite('when file shares have been setup before', () => {
      setup(async () => {
        smbBrowserProxy.anySmbMounted = true;
      });

      test('File shares row is visible', async () => {
        await createFilesSettingsCard();
        await smbBrowserProxy.whenCalled('hasAnySmbMountedBefore');

        const smbSharesLinkRow =
            filesSettingsCard.shadowRoot!.querySelector('#smbSharesRow');
        assertTrue(isVisible(smbSharesLinkRow));
      });

      test('Add file shares row is not visible', async () => {
        await createFilesSettingsCard();
        await smbBrowserProxy.whenCalled('hasAnySmbMountedBefore');

        const addSmbSharesRow =
            filesSettingsCard.shadowRoot!.querySelector('#addSmbSharesRow');
        assertFalse(isVisible(addSmbSharesRow));
      });

      test(
          'File shares row is focused when returning from subpage',
          async () => {
            await createFilesSettingsCard();

            await assertSubpageTriggerFocused(
                '#smbSharesRow', routes.SMB_SHARES);
          });
    });
  } else {
    test('File shares row is visible', async () => {
      await createFilesSettingsCard();
      const smbSharesLinkRow =
          filesSettingsCard.shadowRoot!.querySelector('#smbSharesRow');
      assertTrue(isVisible(smbSharesLinkRow));
    });

    test('Add file shares row is not visible', async () => {
      await createFilesSettingsCard();
      const addSmbSharesRow =
          filesSettingsCard.shadowRoot!.querySelector('#addSmbSharesRow');
      assertFalse(isVisible(addSmbSharesRow));
    });

    test('File shares row is focused when returning from subpage', async () => {
      await createFilesSettingsCard();

      await assertSubpageTriggerFocused('#smbSharesRow', routes.SMB_SHARES);
    });
  }
});