chromium/chrome/test/data/webui/chromeos/settings/os_printing_page/cups_printer_dialog_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 {AddPrinterManuallyDialogElement, AddPrinterManufacturerModelDialogElement, CupsPrintersBrowserProxyImpl, PrinterSetupResult, SettingsCupsAddPrinterDialogElement, SettingsCupsEditPrinterDialogElement, SettingsCupsPrintersElement} from 'chrome://os-settings/lazy_load.js';
import {CrInputElement, CrSearchableDropDownElement, Router, routes} from 'chrome://os-settings/os_settings.js';
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
import {NetworkStateProperties} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {ConnectionStateType, NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
import {DomIf, 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 {flushTasks} from 'chrome://webui-test/polymer_test_util.js';

import {FakeMetricsPrivate} from '../fake_metrics_private.js';

import {createCupsPrinterInfo} from './cups_printer_test_utils.js';
import {TestCupsPrintersBrowserProxy} from './test_cups_printers_browser_proxy.js';

/*
 * Helper function that waits for |getEulaUrl| to get called and then verifies
 * its arguments.
 */
async function verifyGetEulaUrlWasCalled(
    cupsPrintersBrowserProxy: TestCupsPrintersBrowserProxy,
    expectedManufacturer: string, expectedModel: string): Promise<void> {
  const args = await cupsPrintersBrowserProxy.whenCalled('getEulaUrl');
  assertEquals(expectedManufacturer, args[0]);  // ppdManufacturer
  assertEquals(expectedModel, args[1]);         // ppdModel
}

/*
 * Helper function that resets the resolver for |getEulaUrl| and sets the new
 * EULA URL.
 */
function resetGetEulaUrl(
    cupsPrintersBrowserProxy: TestCupsPrintersBrowserProxy,
    eulaUrl: string): void {
  cupsPrintersBrowserProxy.resetResolver('getEulaUrl');
  cupsPrintersBrowserProxy.setEulaUrl(eulaUrl);
}

suite('CupsAddPrinterDialogTests', () => {
  let page: SettingsCupsPrintersElement;
  let dialog: SettingsCupsAddPrinterDialogElement;
  let cupsPrintersBrowserProxy: TestCupsPrintersBrowserProxy;

  function fillAddManuallyDialog(addDialog: AddPrinterManuallyDialogElement):
      void {
    const name = addDialog.shadowRoot!.querySelector<HTMLInputElement>(
        '#printerNameInput');
    const address = addDialog.shadowRoot!.querySelector<HTMLInputElement>(
        '#printerAddressInput');

    assertTrue(!!name);
    name.value = 'Test printer';

    assertTrue(!!address);
    address.value = '127.0.0.1';

    const addButton = addDialog.shadowRoot!.querySelector<HTMLButtonElement>(
        '#addPrinterButton');
    assertTrue(!!addButton);
    assertFalse(addButton.disabled);
  }

  function clickAddButton(dialog: AddPrinterManuallyDialogElement) {
    assertTrue(!!dialog, 'Dialog is null for add');
    const addButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!addButton, 'Button is null');
    addButton.click();
  }

  function clickCancelButton(dialog: AddPrinterManufacturerModelDialogElement) {
    assertTrue(!!dialog, 'Dialog is null for cancel');
    const cancelButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.cancel-button');
    assertTrue(!!cancelButton, 'Button is null');
    cancelButton.click();
  }

  function canAddPrinter(
      dialog: AddPrinterManuallyDialogElement, name: string, address: string) {
    dialog.newPrinter.printerName = name;
    dialog.newPrinter.printerAddress = address;
    return dialog['canAddPrinter_']();
  }

  function mockAddPrinterInputKeyboardPress(crInputId: string) {
    // Start in add manual dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);

    // Test that pressing Enter before all the fields are populated does not
    // advance to the next dialog.
    const input = addDialog.shadowRoot!.querySelector(crInputId);
    assertTrue(!!input);
    keyEventOn(input, 'keypress', /*keycode=*/ 13, [], 'Enter');
    flush();

    assertNull(dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog'));
    assertFalse(dialog.get('showManufacturerDialog_'));
    assertTrue(dialog.get('showManuallyAddDialog_'));

    // Add valid input into the dialog
    fillAddManuallyDialog(addDialog);

    // Test that key press on random key while in input field is not accepted as
    // as valid Enter press.
    keyEventOn(input, 'keypress', /*keycode=*/ 16, [], 'Shift');
    flush();

    assertNull(dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog'));
    assertFalse(dialog.get('showManufacturerDialog_'));
    assertTrue(dialog.get('showManuallyAddDialog_'));

    // Now test Enter press with valid input.
    keyEventOn(input, 'keypress', /*keycode=*/ 13, [], 'Enter');
    flush();
  }

  setup(() => {
    cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();
    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
        cupsPrintersBrowserProxy);

    page = document.createElement('settings-cups-printers');
    // TODO(jimmyxgong): Remove this line when the feature flag is removed.
    page.set('enableUpdatedUi_', false);
    document.body.appendChild(page);
    assertTrue(!!page);
    const element =
        page.shadowRoot!.querySelector('settings-cups-add-printer-dialog');
    assertTrue(!!element);
    dialog = element;

    dialog.open();
    return flushTasks();
  });

  teardown(() => {
    cupsPrintersBrowserProxy.reset();
    page.remove();
    dialog.remove();
  });

  test('ValidIPV4', () => {
    const dialog = document.createElement('add-printer-manually-dialog');
    assertTrue(canAddPrinter(dialog, 'Test printer', '127.0.0.1'));
  });

  test('ValidIPV4WithPort', () => {
    const dialog = document.createElement('add-printer-manually-dialog');
    assertTrue(canAddPrinter(dialog, 'Test printer', '127.0.0.1:1234'));
  });

  test('ValidIPV6', () => {
    const dialog = document.createElement('add-printer-manually-dialog');

    // Test the full ipv6 address scheme.
    assertTrue(canAddPrinter(dialog, 'Test printer', '1:2:a3:ff4:5:6:7:8'));

    // Test the shorthand prefix scheme.
    assertTrue(canAddPrinter(dialog, 'Test printer', '::255'));

    // Test the shorthand suffix scheme.
    assertTrue(canAddPrinter(dialog, 'Test printer', '1::'));
  });

  test('ValidIPV6WithPort', () => {
    const dialog = document.createElement('add-printer-manually-dialog');

    assertTrue(canAddPrinter(dialog, 'Test printer', '[1:2:aa2:4]:12'));
    assertTrue(canAddPrinter(dialog, 'Test printer', '[::255]:54'));
    assertTrue(canAddPrinter(dialog, 'Test printer', '[1::]:7899'));
  });

  test('InvalidIPV6', () => {
    const dialog = document.createElement('add-printer-manually-dialog');

    assertFalse(canAddPrinter(dialog, 'Test printer', '1:2:3:4:5:6:7:8:9'));
    assertFalse(canAddPrinter(dialog, 'Test printer', '1:2:3:aa:a1245:2'));
    assertFalse(canAddPrinter(dialog, 'Test printer', '1:2:3:za:2'));
    assertFalse(canAddPrinter(dialog, 'Test printer', '1:::22'));
    assertFalse(canAddPrinter(dialog, 'Test printer', '1::2::3'));
  });

  test('ValidHostname', () => {
    const dialog = document.createElement('add-printer-manually-dialog');

    assertTrue(canAddPrinter(dialog, 'Test printer', 'hello-world.com'));
    assertTrue(canAddPrinter(dialog, 'Test printer', 'hello.world.com:12345'));
  });

  test('InvalidHostname', () => {
    const dialog = document.createElement('add-printer-manually-dialog');

    assertFalse(canAddPrinter(dialog, 'Test printer', 'helloworld!123.com'));
    assertFalse(canAddPrinter(dialog, 'Test printer', 'helloworld123-.com'));
    assertFalse(canAddPrinter(dialog, 'Test printer', '-helloworld123.com'));
  });

  test('AddPrinterManuallySuccess', async () => {
    const fakeMetricsPrivate = new FakeMetricsPrivate();
    chrome.metricsPrivate = fakeMetricsPrivate;

    // Starts in add manual dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();

    // Fill the printer dialog with default printer details.
    fillAddManuallyDialog(addDialog);

    // Make the PPD resolved true so the printer add is successful.
    cupsPrintersBrowserProxy.setPpdReferenceResolved(true);

    // Attempt to add the printer.
    const button = addDialog.shadowRoot!.querySelector<HTMLButtonElement>(
        '.action-button');
    assertTrue(!!button);
    button.click();
    await cupsPrintersBrowserProxy.whenCalled('addCupsPrinter');
    await flushTasks();

    // Verify the dialog closes after successful add.
    const addPrinterDialog =
        addDialog.shadowRoot!.querySelector('add-printer-dialog');
    assertTrue(!!addPrinterDialog);
    const crDialog =
        addPrinterDialog.shadowRoot!.querySelector<CrDialogElement>(
            'cr-dialog');
    assertTrue(!!crDialog);
    assertFalse(crDialog.open);

    // Record the success to metrics.
    assertEquals(
        1,
        fakeMetricsPrivate.countBoolean(
            'Printing.CUPS.AddPrinterManuallyResult', true));
  });

  /**
   * Test that clicking on Add opens the model select page.
   */
  test('ValidAddOpensModelSelection', async () => {
    // Starts in add manual dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();
    fillAddManuallyDialog(addDialog);

    const button = addDialog.shadowRoot!.querySelector<HTMLButtonElement>(
        '.action-button');
    assertTrue(!!button);
    button.click();
    flush();

    // Upon rejection, show model.
    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    await flushTasks();
    // Showing model selection.
    assertTrue(!!dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog'));

    assertTrue(dialog.get('showManufacturerDialog_'));
    assertFalse(dialog.get('showManuallyAddDialog_'));
  });

  /**
   * Test that when getPrinterInfo fails for a generic reason, the general error
   * message is shown.
   */
  test('GetPrinterInfoFailsGeneralError', async () => {
    const fakeMetricsPrivate = new FakeMetricsPrivate();
    chrome.metricsPrivate = fakeMetricsPrivate;

    // Starts in add manual dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();

    fillAddManuallyDialog(addDialog);

    // Make the getPrinterInfo fail for a generic error.
    cupsPrintersBrowserProxy.setGetPrinterInfoResult(
        PrinterSetupResult.FATAL_ERROR);

    // Attempt to add the printer.
    const button = addDialog.shadowRoot!.querySelector<HTMLButtonElement>(
        '.action-button');
    assertTrue(!!button);
    button.click();
    flush();

    // Upon rejection, show model.
    await cupsPrintersBrowserProxy.whenCalled('getPrinterInfo');
    // The general error should be showing.
    assertTrue(!!addDialog.get('errorText_'));
    const generalErrorElement =
        addDialog.shadowRoot!.querySelector('printer-dialog-error');
    assertTrue(!!generalErrorElement);
    const errorContainer =
        generalErrorElement.shadowRoot!.querySelector<HTMLElement>(
            '#error-container');
    assertTrue(!!errorContainer);
    assertFalse(errorContainer.hidden);
    assertEquals(
        1,
        fakeMetricsPrivate.countBoolean(
            'Printing.CUPS.AddPrinterManuallyResult', false));
  });

  /**
   * Test that when getPrinterInfo fails for an unreachable printer, the
   printer
   * address field is marked as invalid.
   */
  test('GetPrinterInfoFailsUnreachableError', async () => {
    const fakeMetricsPrivate = new FakeMetricsPrivate();
    chrome.metricsPrivate = fakeMetricsPrivate;

    // Starts in add manual dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();

    fillAddManuallyDialog(addDialog);

    // Make the getPrinterInfo fail for an unreachable printer.
    cupsPrintersBrowserProxy.setGetPrinterInfoResult(
        PrinterSetupResult.PRINTER_UNREACHABLE);

    // Attempt to add the printer.
    const button = addDialog.shadowRoot!.querySelector<HTMLButtonElement>(
        '.action-button');
    assertTrue(!!button);
    button.click();
    flush();

    // Upon rejection, show model.
    await cupsPrintersBrowserProxy.whenCalled('getPrinterInfo');

    // The printer address input should be marked as invalid.
    const printerAddressInput =
        addDialog.shadowRoot!.querySelector<CrInputElement>(
            '#printerAddressInput');
    assertTrue(!!printerAddressInput);
    assertTrue(printerAddressInput.invalid);
    assertEquals(
        1,
        fakeMetricsPrivate.countBoolean(
            'Printing.CUPS.AddPrinterManuallyResult', false));
  });

  /**
   * Test that getModels isn't called with a blank query.
   */
  test('NoBlankQueries', async () => {
    // Starts in add manual dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();
    fillAddManuallyDialog(addDialog);

    cupsPrintersBrowserProxy.manufacturers = {
      success: false,
      manufacturers: ['ManufacturerA', 'ManufacturerB', 'Chromites'],
    };
    const button = addDialog.shadowRoot!.querySelector<HTMLButtonElement>(
        '.action-button');
    assertTrue(!!button);
    button.click();
    flush();

    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');

    // Verify that getCupsPrinterModelList is not called.
    assertEquals(
        0, cupsPrintersBrowserProxy.getCallCount('getCupsPrinterModelsList'));

    const modelDialog = dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog');

    // Manufacturer dialog has been rendered and the model list was not
    // requested.  We're done.
    assertTrue(!!modelDialog);
  });

  /**
   * Test that dialog cancellation is logged from the manufacturer screen for
   * IPP printers.
   */
  test('LogDialogCancelledIpp', async () => {
    const makeAndModel = 'Printer Make And Model';
    // Start on add manual dialog.
    dialog.dispatchEvent(new CustomEvent('open-manually-add-printer-dialog'));
    flush();

    // Populate the printer object.
    dialog.newPrinter = {
      isManaged: false,
      ppdManufacturer: '',
      ppdModel: '',
      printerAddress: '192.168.1.13',
      printerDescription: '',
      printerId: '',
      printerMakeAndModel: '',
      printerName: 'Test Printer',
      printerPPDPath: '',
      printerPpdReference: {
        userSuppliedPpdUrl: '',
        effectiveMakeAndModel: '',
        autoconf: false,
      },
      printerProtocol: 'ipps',
      printerQueue: 'moreinfohere',
      printServerUri: '',
    };

    // Seed the getPrinterInfo response.  We detect the make and model but it is
    // not an autoconf printer.
    cupsPrintersBrowserProxy.printerInfo = {
      autoconf: false,
      makeAndModel,
      ppdRefUserSuppliedPpdUrl: 'ppd url',
      ppdRefEffectiveMakeAndModel: 'Effective Make And Model',
      ppdReferenceResolved: false,
    };

    // Press the add button to advance dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();
    clickAddButton(addDialog);

    // Click cancel on the manufacturer dialog when it shows up then verify
    // cancel was called with the appropriate parameters.
    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    flush();
    // Cancel setup with the cancel button.
    const modelDialog = dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog');
    assertTrue(!!modelDialog);
    clickCancelButton(modelDialog);
    const printer =
        await cupsPrintersBrowserProxy.whenCalled('cancelPrinterSetUp');
    assertTrue(!!printer, 'New printer is null');
    assertEquals(makeAndModel, printer.printerMakeAndModel);
  });

  /**
   * Test that we are checking if a printer model has an EULA upon a model
   * change.
   */
  test('getEulaUrlGetsCalledOnModelChange', async () => {
    // Start in add manual dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();
    fillAddManuallyDialog(addDialog);

    const button = addDialog.shadowRoot!.querySelector<HTMLButtonElement>(
        '.action-button');
    assertTrue(!!button);
    button.click();
    flush();

    const expectedEulaLink = 'chrome://os-credits/#google';
    const expectedManufacturer = 'Google';
    const expectedModel = 'printer';
    const expectedModel2 = 'newPrinter';
    const expectedModel3 = 'newPrinter2';

    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    const modelDialog = dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog');
    assertTrue(!!modelDialog);

    const urlElement =
        modelDialog.shadowRoot!.querySelector<HTMLElement>('#eulaUrl');
    assertTrue(!!urlElement);
    // Check that the EULA text is not shown.
    assertTrue(urlElement.hidden);

    cupsPrintersBrowserProxy.setEulaUrl(expectedEulaLink);

    const manufacturerDropdown =
        modelDialog.shadowRoot!.querySelector<CrSearchableDropDownElement>(
            '#manufacturerDropdown');
    assertTrue(!!manufacturerDropdown);
    manufacturerDropdown.value = expectedManufacturer;
    const modelDropdown =
        modelDialog.shadowRoot!.querySelector<CrSearchableDropDownElement>(
            '#modelDropdown');
    assertTrue(!!modelDropdown);
    modelDropdown.value = expectedModel;
    await verifyGetEulaUrlWasCalled(
        cupsPrintersBrowserProxy, expectedManufacturer, expectedModel);

    // Check that the EULA text is shown.
    assertFalse(urlElement.hidden);
    let anchor = urlElement.querySelector('a');
    assertTrue(!!anchor);
    assertEquals(expectedEulaLink, anchor.href);

    resetGetEulaUrl(cupsPrintersBrowserProxy, '' /* eulaUrl */);

    // Change ppdModel and expect |getEulaUrl| to be called again.
    modelDropdown.value = expectedModel2;
    await verifyGetEulaUrlWasCalled(
        cupsPrintersBrowserProxy, expectedManufacturer, expectedModel2);

    // Check that the EULA text is hidden.
    assertTrue(urlElement.hidden);

    resetGetEulaUrl(cupsPrintersBrowserProxy, expectedEulaLink);

    // Change ppdModel and expect |getEulaUrl| to be called again.
    modelDropdown.value = expectedModel3;
    await verifyGetEulaUrlWasCalled(
        cupsPrintersBrowserProxy, expectedManufacturer, expectedModel3);
    assertFalse(urlElement.hidden);
    anchor = urlElement.querySelector('a');
    assertTrue(!!anchor);
    assertEquals(expectedEulaLink, anchor.href);
  });

  /**
   * Test that the add button of the manufacturer dialog is disabled after
   * clicking it.
   */
  test('AddButtonDisabledAfterClicking', async () => {
    // From the add manually dialog, click the add button to advance to the
    // manufacturer dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();
    fillAddManuallyDialog(addDialog);
    clickAddButton(addDialog);
    flush();

    // Click the add button on the manufacturer dialog and then verify it is
    // disabled.
    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    const manufacturerDialog = dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog');
    assertTrue(!!manufacturerDialog);

    // Populate the manufacturer and model fields to enable the add
    // button.
    const manufacturerDropdown =
        manufacturerDialog.shadowRoot!
            .querySelector<CrSearchableDropDownElement>(
                '#manufacturerDropdown');
    assertTrue(!!manufacturerDropdown);
    manufacturerDropdown.value = 'make';
    const modelDropdown =
        manufacturerDialog.shadowRoot!
            .querySelector<CrSearchableDropDownElement>('#modelDropdown');
    assertTrue(!!modelDropdown);
    modelDropdown.value = 'model';

    const addButton =
        manufacturerDialog.shadowRoot!.querySelector<HTMLButtonElement>(
            '#addPrinterButton');
    assertTrue(!!addButton);
    assertFalse(addButton.disabled);
    addButton.click();
    assertTrue(addButton.disabled);
  });

  /**
   * The following tests check that clicking Enter button on the keyboard
   from
   * each input text field on the add-printer-manually-dialog will advance to
   * the next dialog.
   */
  test('PressEnterInPrinterNameInput', async () => {
    mockAddPrinterInputKeyboardPress('#printerNameInput');

    // Upon rejection, show model.
    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    await flushTasks();
    // Showing model selection.
    assertTrue(!!dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog'));
    assertTrue(dialog.get('showManufacturerDialog_'));
    assertFalse(dialog.get('showManuallyAddDialog_'));
  });

  test('PressEnterInPrinterAddressInput', async () => {
    mockAddPrinterInputKeyboardPress('#printerAddressInput');

    // Upon rejection, show model.
    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    await flushTasks();
    // Showing model selection.
    assertNull(
        dialog.shadowRoot!.querySelector('add-printer-configuring-dialog'));
    assertTrue(dialog.get('showManufacturerDialog_'));
    assertFalse(dialog.get('showManuallyAddDialog_'));
  });

  test('PressEnterInPrinterQueueInput', async () => {
    mockAddPrinterInputKeyboardPress('#printerQueueInput');

    // Upon rejection, show model.
    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    await flushTasks();
    // Showing model selection.
    assertTrue(!!dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog'));
    assertTrue(dialog.get('showManufacturerDialog_'));
    assertFalse(dialog.get('showManuallyAddDialog_'));
  });

  /**
   * Test that the add button of the manufacturer dialog is disabled when the
   * manufacturer or model dropdown has an incorrect value.
   */
  test('AddButtonDisabledAfterClicking', async () => {
    // From the add manually dialog, click the add button to advance to the
    // manufacturer dialog.
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    flush();
    fillAddManuallyDialog(addDialog);
    clickAddButton(addDialog);
    flush();

    await cupsPrintersBrowserProxy.whenCalled(
        'getCupsPrinterManufacturersList');
    const manufacturerDialog = dialog.shadowRoot!.querySelector(
        'add-printer-manufacturer-model-dialog');
    assertTrue(!!manufacturerDialog);

    const manufacturerDropdown =
        manufacturerDialog.shadowRoot!
            .querySelector<CrSearchableDropDownElement>(
                '#manufacturerDropdown');
    const modelDropdown =
        manufacturerDialog.shadowRoot!
            .querySelector<CrSearchableDropDownElement>('#modelDropdown');
    const addButton =
        manufacturerDialog.shadowRoot!.querySelector<HTMLButtonElement>(
            '#addPrinterButton');

    assertTrue(!!manufacturerDropdown);
    assertTrue(!!modelDropdown);
    assertTrue(!!addButton);
    // Set the starting values for manufacturer and model dropdown.
    manufacturerDropdown.value = 'make';
    modelDropdown.value = 'model';
    assertFalse(addButton.disabled);

    // Mimic typing in random input. Make sure the Add button becomes
    // disabled.
    let searchElement =
        manufacturerDropdown.shadowRoot!.querySelector<HTMLInputElement>(
            '#search');
    assertTrue(!!searchElement);
    searchElement.value = 'hlrRkJQkNsh';
    searchElement.dispatchEvent(new CustomEvent('input'));
    assertTrue(addButton.disabled);

    // Then mimic typing in the original value to re-enable the Add
    // button.
    searchElement =
        manufacturerDropdown.shadowRoot!.querySelector<HTMLInputElement>(
            '#search');
    assertTrue(!!searchElement);
    searchElement.value = 'make';
    searchElement.dispatchEvent(new CustomEvent('input'));
    assertFalse(addButton.disabled);

    // Mimic typing in random input. Make sure the Add button becomes
    // disabled.
    searchElement =
        modelDropdown.shadowRoot!.querySelector<HTMLInputElement>('#search');
    assertTrue(!!searchElement);
    searchElement.value = 'hlrRkJQkNsh';
    searchElement.dispatchEvent(new CustomEvent('input'));
    assertTrue(addButton.disabled);

    // Then mimic typing in the original value to re-enable the Add
    // button.
    searchElement =
        modelDropdown.shadowRoot!.querySelector<HTMLInputElement>('#search');
    assertTrue(!!searchElement);
    searchElement.value = 'model';
    searchElement.dispatchEvent(new CustomEvent('input'));
    assertFalse(addButton.disabled);
  });

  test('Queue input is hidden when protocol is App Socket', () => {
    const addDialog =
        dialog.shadowRoot!.querySelector('add-printer-manually-dialog');
    assertTrue(!!addDialog);
    let printerQueueInput =
        addDialog.shadowRoot!.querySelector('#printerQueueInput');
    assertTrue(!!printerQueueInput);
    const select = addDialog.shadowRoot!.querySelector('select');
    assertTrue(!!select);

    select.value = 'socket';
    select.dispatchEvent(new CustomEvent('change', {'bubbles': true}));
    flush();

    printerQueueInput =
        addDialog.shadowRoot!.querySelector('#printerQueueInput');
    assertNull(printerQueueInput);

    select.value = 'http';
    select.dispatchEvent(new CustomEvent('change', {'bubbles': true}));
    flush();

    printerQueueInput =
        addDialog.shadowRoot!.querySelector('#printerQueueInput');
    assertTrue(!!printerQueueInput);
  });
});

suite('EditPrinterDialog', () => {
  let page: SettingsCupsPrintersElement;
  let dialog: SettingsCupsEditPrinterDialogElement;
  let cupsPrintersBrowserProxy: TestCupsPrintersBrowserProxy;
  let wifi1: NetworkStateProperties;

  setup(async () => {
    cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();

    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
        cupsPrintersBrowserProxy);

    // Simulate internet connection.
    wifi1 = OncMojo.getDefaultNetworkState(NetworkType.kWiFi, 'wifi1');
    wifi1.connectionState = ConnectionStateType.kOnline;

    Router.getInstance().navigateTo(routes.CUPS_PRINTERS);

    page = document.createElement('settings-cups-printers');
    document.body.appendChild(page);
    assertTrue(!!page);
    page.onActiveNetworksChanged([wifi1]);
    await flushTasks();
  });

  teardown(() => {
    cupsPrintersBrowserProxy.reset();
    page.remove();
    dialog.remove();
  });

  function clickSaveButton(dialog: SettingsCupsEditPrinterDialogElement) {
    assertTrue(!!dialog, 'Dialog is null for save');
    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    dialog.set('printerInfoChanged_', true);
    assertTrue(!!saveButton, 'Button is null');
    assertFalse(saveButton.disabled);
    saveButton.click();
  }

  function clickCancelButton(dialog: SettingsCupsEditPrinterDialogElement) {
    assertTrue(!!dialog, 'Dialog is null for cancel');
    const cancelButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.cancel-button');
    assertTrue(!!cancelButton, 'Button is null');
    cancelButton.click();
  }

  /**
   * Initializes a printer and sets that printer as the printer to be edited in
   * the edit dialog. Opens the edit dialog.
   */
  async function initializeAndOpenEditDialog(
      name: string, address: string, id: string, autoconf: boolean,
      manufacturer: string, model: string, protocol: string,
      serverAddress: string): Promise<void> {
    page.activePrinter = createCupsPrinterInfo(name, address, id);
    page.activePrinter.printerPpdReference.autoconf = autoconf;
    page.activePrinter.printerProtocol = protocol;
    page.activePrinter.printServerUri = serverAddress;
    cupsPrintersBrowserProxy.printerPpdMakeModel = {
      ppdManufacturer: manufacturer,
      ppdModel: model,
    };
    // Trigger the edit dialog to open.
    page.dispatchEvent(new CustomEvent('edit-cups-printer-details'));
    flush();
    const element =
        page.shadowRoot!.querySelector('settings-cups-edit-printer-dialog');
    assertTrue(!!element);
    dialog = element;
    // This proxy function gets called whenever the edit dialog is initialized.
    await cupsPrintersBrowserProxy.whenCalled('getCupsPrinterModelsList');
  }

  /**
   * Test that USB printers can be edited.
   */
  test('USBPrinterCanBeEdited', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'usb', /*serverAddress=*/ '');
    // Assert that the protocol is USB.
    const selectElement =
        dialog.shadowRoot!.querySelector<HTMLSelectElement>('.md-select');
    assertTrue(!!selectElement);
    assertEquals('usb', selectElement.value);

    // Edit the printer name.
    const nameField = dialog.shadowRoot!.querySelector<HTMLInputElement>(
        '.printer-name-input');
    assertTrue(!!nameField);
    nameField.value = 'edited printer';
    nameField.dispatchEvent(new CustomEvent('input'));

    // Assert that the "Save" button is enabled.
    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertFalse(saveButton.disabled);
  });

  /**
   * Test that the save button is disabled when the printer address or name is
   * invalid.
   */
  test('EditPrinter', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const printerName =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerName');
    const printerAddress =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerAddress');
    assertTrue(!!printerName);
    assertTrue(!!printerAddress);

    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertTrue(saveButton.disabled);

    // Change printer name to something valid.
    printerName.value = 'new printer name';
    printerName.dispatchEvent(new CustomEvent('input'));
    assertFalse(saveButton.disabled);

    // Change printer address to something invalid.
    printerAddress.value = 'abcdef:';
    assertTrue(saveButton.disabled);

    // Change back to something valid.
    printerAddress.value = 'abcdef:1234';
    assertFalse(saveButton.disabled);

    // Change printer name to empty.
    printerName.value = '';
    assertTrue(saveButton.disabled);
  });

  /**
   * Test that closing the dialog does not persist the edits.
   */
  test('CloseEditDialogDoesNotModifyActivePrinter', async () => {
    const expectedName = 'Test Printer';
    const expectedAddress = '1.1.1.1';
    const expectedId = 'ID1';
    const expectedProtocol = 'ipp';
    await initializeAndOpenEditDialog(
        /*name=*/ expectedName, /*address=*/ expectedAddress,
        /*id=*/ expectedId, /*autoconf=*/ false,
        /*manufacturer=*/ 'make', /*model=*/ 'model',
        /*protocol=*/ expectedProtocol, /*serverAddress=*/ '');
    const nameField = dialog.shadowRoot!.querySelector<HTMLInputElement>(
        '.printer-name-input');
    assertTrue(!!nameField);
    nameField.value = 'edited printer name';

    const addressField =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerAddress');
    assertTrue(!!addressField);
    addressField.value = '9.9.9.9';

    const protocolField =
        dialog.shadowRoot!.querySelector<HTMLSelectElement>('.md-select');
    assertTrue(!!protocolField);
    protocolField.value = 'http';

    clickCancelButton(dialog);

    // Assert that activePrinter properties were not changed.
    assertEquals(expectedName, dialog.activePrinter.printerName);
    assertEquals(expectedAddress, dialog.activePrinter.printerAddress);
    assertEquals(expectedProtocol, dialog.activePrinter.printerProtocol);
  });

  /**
   * Test that editing the name field results in properly saving the new name.
   */
  test('TestEditNameAndSave', async () => {
    const expectedName = 'editedName';
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const nameField = dialog.shadowRoot!.querySelector<HTMLInputElement>(
        '.printer-name-input');
    assertTrue(!!nameField);
    nameField.value = expectedName;

    flush();
    clickSaveButton(dialog);
    await cupsPrintersBrowserProxy.whenCalled('updateCupsPrinter');
    assertEquals(expectedName, dialog.activePrinter.printerName);
  });

  /**
   * Test that editing various fields results in properly saving the new
   * changes.
   */
  test('TestEditFieldsAndSave', async () => {
    const expectedAddress = '9.9.9.9';
    const expectedQueue = 'editedQueue';
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');

    // Editing more than just the printer name requires reconfiguring the
    // printer.
    const addressField =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerAddress');
    assertTrue(!!addressField);
    addressField.value = expectedAddress;

    const queueField =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerQueue');
    assertTrue(!!queueField);
    queueField.value = expectedQueue;

    clickSaveButton(dialog);
    await cupsPrintersBrowserProxy.whenCalled('reconfigureCupsPrinter');
    assertEquals(expectedAddress, dialog.activePrinter.printerAddress);
    assertEquals(expectedQueue, dialog.activePrinter.printerQueue);
  });

  /**
   * Test that editing an autoconf printer saves correctly.
   */
  test('TestEditAutoConfFieldsAndSave', async () => {
    const expectedAddress = '9.9.9.9';
    const expectedQueue = 'editedQueue';
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ true, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    // Editing more than just the printer name requires reconfiguring the
    // printer.
    const addressField =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerAddress');
    assertTrue(!!addressField);
    addressField.value = expectedAddress;

    const queueField =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerQueue');
    assertTrue(!!queueField);
    queueField.value = expectedQueue;

    clickSaveButton(dialog);
    await cupsPrintersBrowserProxy.whenCalled('reconfigureCupsPrinter');
    assertEquals(expectedAddress, dialog.activePrinter.printerAddress);
    assertEquals(expectedQueue, dialog.activePrinter.printerQueue);
  });

  /**
   * Test that non-autoconf printers can select the make and model dropdowns.
   */
  test('TestNonAutoConfPrintersCanSelectManufactureAndModel', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    // Assert that the manufacturer and model drop-downs are shown.
    const element =
        dialog.shadowRoot!.querySelector<HTMLElement>('#makeAndModelSection');
    assertTrue(!!element);
    assertFalse(element.hidden);
  });

  /**
   * Test that autoconf printers cannot select their make/model
   */
  test('TestAutoConfPrintersCannotSelectManufactureAndModel', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ true, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    // Assert that the manufacturer and model drop-downs are hidden.
    const element =
        dialog.shadowRoot!.querySelector<DomIf>('#makeAndModelSection');
    assertTrue(!!element);
    assertTrue(!element.if);
  });

  /**
   * Test that changing the name enables the save button.
   */
  test('TestChangingNameEnablesSaveButton', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertTrue(saveButton.disabled);

    const nameField = dialog.shadowRoot!.querySelector<HTMLInputElement>(
        '.printer-name-input');
    assertTrue(!!nameField);
    nameField.value = 'edited printer';
    nameField.dispatchEvent(new CustomEvent('input'));

    assertFalse(saveButton.disabled);
  });

  /**
   * Test that changing the address enables the save button.
   */
  test('TestChangingAddressEnablesSaveButton', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertTrue(saveButton.disabled);

    const addressField =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerAddress');
    assertTrue(!!addressField);
    addressField.value = 'newAddress:789';
    addressField.dispatchEvent(new CustomEvent('input'));

    assertFalse(saveButton.disabled);
  });

  /**
   * Test that changing the queue enables the save button.
   */
  test('TestChangingQueueEnablesSaveButton', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertTrue(saveButton.disabled);

    const queueField =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerQueue');
    assertTrue(!!queueField);
    queueField.value = 'newqueueinfo';
    queueField.dispatchEvent(new CustomEvent('input'));

    assertFalse(saveButton.disabled);
  });

  /**
   * Test that changing the protocol enables the save button.
   */
  test('TestChangingProtocolEnablesSaveButton', async () => {
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertTrue(saveButton.disabled);

    const dropDown =
        dialog.shadowRoot!.querySelector<HTMLSelectElement>('.md-select');
    assertTrue(!!dropDown);
    dropDown.value = 'http';
    dropDown.dispatchEvent(new CustomEvent('change', {'bubbles': true}));
    flush();
    assertFalse(saveButton.disabled);
  });

  /**
   * Test that changing the model enables the save button.
   */
  test('TestChangingModelEnablesSaveButton', async () => {
    cupsPrintersBrowserProxy.manufacturers = {
      success: true,
      manufacturers: ['HP'],
    };
    cupsPrintersBrowserProxy.models = {success: true, models: ['HP 910']};
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertTrue(saveButton.disabled);

    const makeDropDown =
        dialog.shadowRoot!.querySelector<CrSearchableDropDownElement>(
            '#printerPPDManufacturer');
    assertTrue(!!makeDropDown);
    makeDropDown.value = 'HP';
    makeDropDown.dispatchEvent(new CustomEvent('change', {'bubbles': true}));

    await cupsPrintersBrowserProxy.whenCalled('getCupsPrinterModelsList');
    // Saving is disabled until a model is selected.
    assertTrue(saveButton.disabled);

    const modelDropDown =
        dialog.shadowRoot!.querySelector<CrSearchableDropDownElement>(
            '#printerPPDModel');
    assertTrue(!!modelDropDown);
    modelDropDown.value = 'HP 910';
    modelDropDown.dispatchEvent(new CustomEvent('change', {'bubbles': true}));

    flush();
    assertFalse(saveButton.disabled);
  });

  /**
   * Test that we are checking if a printer model has an EULA upon a model
   * change.
   */
  test('getEulaUrlGetsCalledOnModelChange', async () => {
    const eulaLink = 'google.com';
    const expectedManufacturer = 'Google';
    const expectedModel = 'model';
    const expectedModel2 = 'newModel';
    const expectedModel3 = 'newModel2';

    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const urlElement =
        dialog.shadowRoot!.querySelector<HTMLElement>('#eulaUrl');
    assertTrue(!!urlElement);
    // Check that the EULA text is hidden.
    assertTrue(urlElement.hidden);

    // 'getEulaUrl' is called as part of the initialization of the dialog,
    // so we have to reset the resolver before the next call.
    resetGetEulaUrl(cupsPrintersBrowserProxy, eulaLink);

    const makeDropDown =
        dialog.shadowRoot!.querySelector<CrSearchableDropDownElement>(
            '#printerPPDManufacturer');
    assertTrue(!!makeDropDown);
    makeDropDown.value = expectedManufacturer;
    const modelDropdown =
        dialog.shadowRoot!.querySelector<CrSearchableDropDownElement>(
            '#printerPPDModel');
    assertTrue(!!modelDropdown);
    modelDropdown.value = expectedModel;

    await verifyGetEulaUrlWasCalled(
        cupsPrintersBrowserProxy, expectedManufacturer, expectedModel);

    // Check that the EULA text is shown.
    assertFalse(urlElement.hidden);

    resetGetEulaUrl(cupsPrintersBrowserProxy, /*eulaUrl=*/ '');

    // Change ppdModel and expect |getEulaUrl| to be called again.
    modelDropdown.value = expectedModel2;
    await verifyGetEulaUrlWasCalled(
        cupsPrintersBrowserProxy, expectedManufacturer, expectedModel2);

    // Check that the EULA text is hidden.
    assertTrue(urlElement.hidden);

    resetGetEulaUrl(cupsPrintersBrowserProxy, eulaLink);

    // Change ppdModel and expect |getEulaUrl| to be called again.
    modelDropdown.value = expectedModel3;
    await verifyGetEulaUrlWasCalled(
        cupsPrintersBrowserProxy, expectedManufacturer, expectedModel3);

    // Check that the EULA text is shown again.
    assertFalse(urlElement.hidden);
  });

  /**
   * Test that editing the name is still supported when offline.
   */
  test('OfflineEdit', async () => {
    // Simulate connecting to a network with no internet connection.
    wifi1.connectionState = ConnectionStateType.kConnected;
    page.onActiveNetworksChanged([wifi1]);
    flush();
    const expectedName = 'editedName';
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ false, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '');
    const nameField = dialog.shadowRoot!.querySelector<HTMLInputElement>(
        '.printer-name-input');
    assertTrue(!!nameField);
    nameField.value = expectedName;
    nameField.dispatchEvent(new CustomEvent('input'));

    flush();

    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertFalse(saveButton.disabled);

    clickSaveButton(dialog);
    await cupsPrintersBrowserProxy.whenCalled('updateCupsPrinter');
    assertEquals(expectedName, dialog.activePrinter.printerName);
  });

  test('PrintServerPrinterEdit', async () => {
    const expectedName = 'edited name';
    await initializeAndOpenEditDialog(
        /*name=*/ 'name', /*address=*/ 'address', /*id=*/ 'id',
        /*autoconf=*/ true, /*manufacturer=*/ 'make',
        /*model=*/ 'model', /*protocol=*/ 'ipp',
        /*serverAddress=*/ 'ipp://192.168.1.1:631');

    // Verify the only the name field is not disabled.
    const printerAddress =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerAddress');
    assertTrue(!!printerAddress);
    assertTrue(printerAddress.disabled);
    const selectElement =
        dialog.shadowRoot!.querySelector<HTMLSelectElement>('.md-select');
    assertTrue(!!selectElement);
    assertTrue(selectElement.disabled);
    const printerQueue =
        dialog.shadowRoot!.querySelector<HTMLInputElement>('#printerQueue');
    assertTrue(!!printerQueue);
    assertTrue(printerQueue.disabled);

    const nameField = dialog.shadowRoot!.querySelector<HTMLInputElement>(
        '.printer-name-input');
    assertTrue(!!nameField);
    assertFalse(nameField.disabled);

    nameField.value = expectedName;
    nameField.dispatchEvent(new CustomEvent('input'));

    flush();

    const saveButton =
        dialog.shadowRoot!.querySelector<HTMLButtonElement>('.action-button');
    assertTrue(!!saveButton);
    assertFalse(saveButton.disabled);

    clickSaveButton(dialog);
    await cupsPrintersBrowserProxy.whenCalled('updateCupsPrinter');
    assertEquals(expectedName, dialog.activePrinter.printerName);
  });
});