chromium/chrome/test/data/webui/chromeos/settings/os_files_page/smb_shares_page_test.ts

// Copyright 2018 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 {AddSmbShareDialogElement, SettingsSmbSharesPageElement, SmbBrowserProxy, SmbBrowserProxyImpl, SmbMountResult} from 'chrome://os-settings/lazy_load.js';
import {CrButtonElement, CrInputElement} from 'chrome://os-settings/os_settings.js';
import {assert} from 'chrome://resources/js/assert.js';
import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';

class TestSmbBrowserProxy extends TestBrowserProxy implements SmbBrowserProxy {
  smbMountResult = SmbMountResult.SUCCESS;
  constructor() {
    super([
      'smbMount',
      'startDiscovery',
    ]);
  }

  smbMount(
      smbUrl: string, smbName: string, username: string, password: string,
      authMethod: string, shouldOpenFileManagerAfterMount: boolean,
      saveCredentials: boolean): Promise<SmbMountResult> {
    this.methodCalled(
        'smbMount', smbUrl, smbName, username, password, authMethod,
        shouldOpenFileManagerAfterMount, saveCredentials);
    return Promise.resolve(this.smbMountResult);
  }

  startDiscovery(): void {
    this.methodCalled('startDiscovery');
  }

  updateCredentials(mountId: string, username: string, password: string): void {
    this.methodCalled('updateCredentials', mountId, username, password);
  }

  hasAnySmbMountedBefore(): Promise<boolean> {
    this.methodCalled('hasAnySmbMountedBefore');
    return Promise.resolve(true);
  }
}

suite('<settings-smb-shares-page>', () => {
  let page: SettingsSmbSharesPageElement;
  let addDialog: AddSmbShareDialogElement|null;
  let smbBrowserProxy: TestSmbBrowserProxy;
  let smbSuccessEventDispatched: boolean = false;

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

    page = document.createElement('settings-smb-shares-page');
    document.body.appendChild(page);

    const button = page.shadowRoot!.querySelector<CrButtonElement>('#addShare');
    assert(button);
    button.click();
    flush();

    addDialog = page.shadowRoot!.querySelector('add-smb-share-dialog');
    assert(addDialog);

    flush();
  });

  teardown(() => {
    page.remove();
    addDialog = null;
  });

  test('AddBecomesEnabled', () => {
    assert(addDialog);
    const url = addDialog.shadowRoot!.querySelector('cr-searchable-drop-down');
    assert(url);

    url.value = 'smb://192.168.1.1/testshare';

    const addButton =
        addDialog.shadowRoot!.querySelector<CrButtonElement>('.action-button');
    assert(addButton);
    assertFalse(addButton.disabled);
  });

  test('AddDisabledWithInvalidUrl', () => {
    assert(addDialog);
    const url = addDialog.shadowRoot!.querySelector('cr-searchable-drop-down');
    assert(url);
    const addButton =
        addDialog.shadowRoot!.querySelector<CrButtonElement>('.action-button');
    assert(addButton);

    url.value = '';
    assertTrue(addButton.disabled);

    // Invalid scheme (must start with smb:// or \\)
    url.value = 'foobar';
    assertTrue(addButton.disabled);
    url.value = 'foo\\\\bar\\baz';
    assertTrue(addButton.disabled);
    url.value = 'smb:/foo/bar';
    assertTrue(addButton.disabled);

    // Incomplete (must be of the form \\server\share)
    url.value = '\\\\foo';
    assertTrue(addButton.disabled);
    url.value = '\\\\foo\\';
    assertTrue(addButton.disabled);
    url.value = '\\\\foo\\\\';
    assertTrue(addButton.disabled);

    // Incomplete (must be of the form smb://server/share)
    url.value = 'smb://';
    assertTrue(addButton.disabled);
    url.value = 'smb://foo';
    assertTrue(addButton.disabled);
    url.value = 'smb://foo/';
    assertTrue(addButton.disabled);

    // Valid URLs.
    url.value = '\\\\foo\\bar';
    assertFalse(addButton.disabled);
    url.value = 'smb://foo/bar';
    assertFalse(addButton.disabled);
  });

  test('ClickAdd', async () => {
    const expectedSmbUrl = 'smb://192.168.1.1/testshare';
    const expectedSmbName = 'testname';
    const expectedUsername = 'username';
    const expectedPassword = 'password';
    const expectedAuthMethod = 'credentials';
    const expectedShouldOpenFileManager = false;

    assert(addDialog);
    const url = addDialog.shadowRoot!.querySelector('cr-searchable-drop-down');
    assert(url);
    url.value = expectedSmbUrl;

    const name = addDialog.shadowRoot!.querySelector<CrInputElement>('#name');
    assert(name);
    name.value = expectedSmbName;

    const un = addDialog.shadowRoot!.querySelector<CrInputElement>('#username');
    assert(un);
    un.value = expectedUsername;

    const pw = addDialog.shadowRoot!.querySelector<CrInputElement>('#password');
    assert(pw);
    pw.value = expectedPassword;

    const addButton =
        addDialog.shadowRoot!.querySelector<CrButtonElement>('.action-button');
    assert(addButton);

    addDialog.set('authenticationMethod_', expectedAuthMethod);
    addDialog.shouldOpenFileManagerAfterMount = expectedShouldOpenFileManager;

    smbBrowserProxy.resetResolver('smbMount');
    addButton.click();
    const smbMountArguments = await smbBrowserProxy.whenCalled('smbMount');
    assertEquals(expectedSmbUrl, smbMountArguments[0]);
    assertEquals(expectedSmbName, smbMountArguments[1]);
    assertEquals(expectedUsername, smbMountArguments[2]);
    assertEquals(expectedPassword, smbMountArguments[3]);
    assertEquals(expectedAuthMethod, smbMountArguments[4]);
    assertEquals(expectedShouldOpenFileManager, smbMountArguments[5]);
  });

  test('EventDispatchedAfterClickAddSuccessful', async () => {
    page.addEventListener('smb-successfully-mounted-once', () => {
      smbSuccessEventDispatched = true;
    });

    const expectedSmbUrl = 'smb://192.168.1.1/testshare';
    const expectedSmbName = 'testname';
    const expectedUsername = 'username';
    const expectedPassword = 'password';
    const expectedAuthMethod = 'credentials';
    const expectedShouldOpenFileManager = false;

    assert(addDialog);
    const url = addDialog.shadowRoot!.querySelector('cr-searchable-drop-down');
    assert(url);
    url.value = expectedSmbUrl;

    const name = addDialog.shadowRoot!.querySelector<CrInputElement>('#name');
    assert(name);
    name.value = expectedSmbName;

    const un = addDialog.shadowRoot!.querySelector<CrInputElement>('#username');
    assert(un);
    un.value = expectedUsername;

    const pw = addDialog.shadowRoot!.querySelector<CrInputElement>('#password');
    assert(pw);
    pw.value = expectedPassword;

    const addButton =
        addDialog.shadowRoot!.querySelector<CrButtonElement>('.action-button');
    assert(addButton);

    addDialog.set('authenticationMethod_', expectedAuthMethod);
    addDialog.shouldOpenFileManagerAfterMount = expectedShouldOpenFileManager;

    smbBrowserProxy.resetResolver('smbMount');
    smbBrowserProxy.smbMountResult = SmbMountResult.SUCCESS;
    addButton.click();
    await smbBrowserProxy.whenCalled('smbMount');

    assertTrue(smbSuccessEventDispatched);
  });

  test('StartDiscovery', async () => {
    await smbBrowserProxy.whenCalled('startDiscovery');
  });

  test('ControlledByPolicy', () => {
    const button = page.shadowRoot!.querySelector<CrButtonElement>('#addShare');
    assert(button);

    assertEquals(
        null, page.shadowRoot!.querySelector('cr-policy-pref-indicator'));
    assertFalse(button.disabled);

    page.prefs = {
      network_file_shares: {allowed: {value: false}},
    };
    flush();

    assert(page.shadowRoot!.querySelector('cr-policy-pref-indicator'));
    assertTrue(button.disabled);
  });

  test('AuthenticationSelectorVisibility', () => {
    assert(addDialog);
    const authenticationMethod =
        addDialog.shadowRoot!.querySelector<HTMLElement>(
            '#authentication-method');
    assert(authenticationMethod);

    assertTrue(authenticationMethod.hidden);

    addDialog.set('isKerberosEnabled_', true);

    assertFalse(authenticationMethod.hidden);
  });

  test('AuthenticationSelectorControlsCredentialFields', () => {
    assert(addDialog);
    addDialog.set('isKerberosEnabled_', true);

    assertFalse(
        addDialog.shadowRoot!
            .querySelector<HTMLElement>('#authentication-method')!.hidden);

    const dropDown =
        addDialog.shadowRoot!.querySelector<HTMLSelectElement>('.md-select');
    assert(dropDown);

    const credentials =
        addDialog.shadowRoot!.querySelector<HTMLElement>('#credentials');
    assert(credentials);

    dropDown.value = 'kerberos';
    dropDown.dispatchEvent(new CustomEvent('change'));
    flush();

    assertTrue(credentials.hidden);

    dropDown.value = 'credentials';
    dropDown.dispatchEvent(new CustomEvent('change'));
    flush();

    assertFalse(credentials.hidden);
  });

  test('MostRecentlyUsedUrl', () => {
    const expectedSmbUrl = 'smb://192.168.1.1/testshare';

    page.remove();

    page = document.createElement('settings-smb-shares-page');
    page.prefs = {
      network_file_shares: {
        most_recently_used_url: {value: expectedSmbUrl},
        allowed: {value: true},
      },
    };
    document.body.appendChild(page);

    const button = page.shadowRoot!.querySelector<CrButtonElement>('#addShare');
    assert(button);
    assertFalse(button.disabled);
    button.click();

    flush();

    addDialog = page.shadowRoot!.querySelector('add-smb-share-dialog');
    assert(addDialog);

    flush();

    const openDialogButton =
        page.shadowRoot!.querySelector<CrButtonElement>('#addShare');
    openDialogButton!.click();

    assertEquals(expectedSmbUrl, addDialog.get('mountUrl_'));
    assertEquals(expectedSmbUrl, addDialog.get('mountUrl_'));
  });

  test('InvalidUrlErrorDisablesAddButton', async () => {
    assert(addDialog);
    const url = addDialog.shadowRoot!.querySelector('cr-searchable-drop-down');
    assert(url);
    const addButton =
        addDialog.shadowRoot!.querySelector<CrButtonElement>('.action-button');
    assert(addButton);

    // Invalid URL, but passes regex test.
    url.value = 'smb://foo\\\\/bar';
    assertFalse(addButton.disabled);

    smbBrowserProxy.smbMountResult = SmbMountResult.INVALID_URL;
    addButton.click();

    await new Promise((resolve) => {
      const pollFunc = () => {
        if (url.errorMessage && addButton.disabled) {
          resolve(null);
          return;
        }
        setTimeout(pollFunc, 100);
      };
      // url.errorMessage can't be observed for a change, so instead, poll.
      pollFunc();
    });
  });

  test('LoadingBarDuringDiscovery', async () => {
    assert(addDialog);
    const url = addDialog.shadowRoot!.querySelector('cr-searchable-drop-down');
    assert(url);
    // Loading bar is shown when the page loads.
    assertTrue(url.showLoading);

    await smbBrowserProxy.whenCalled('startDiscovery');

    webUIListenerCallback('on-shares-found', ['smb://foo/bar'], false);
    assertTrue(url.showLoading);

    webUIListenerCallback('on-shares-found', ['smb://foo/bar2'], true);
    assertFalse(url.showLoading);

    assertEquals(2, url.items.length);
  });
});