0

CrOS Settings: Convert apn detail dialog browser test to TS

Bug: b/270728282
Test: browser_tests --gtest_filter="*ApnDetailDialog*"
Change-Id: I1ae97454cfb1fe7212aae8fe26ffb17ecd92293e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4908401
Commit-Queue: Ruma Kesh <rkesh@google.com>
Reviewed-by: Wes Okuhara <wesokuhara@google.com>
Cr-Commit-Position: refs/heads/main@{#1207351}
This commit is contained in:
Ruma Kesh
2023-10-10 00:26:43 +00:00
committed by Chromium LUCI CQ
parent 80516eb1c8
commit adb7d16865
6 changed files with 546 additions and 464 deletions
ash/webui/common/resources/network
chrome

@ -2,4 +2,26 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
export {};
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {ApnProperties} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ApnDetailDialogMode} from './cellular_utils.js';
export class ApnDetailDialog extends I18nMixin
(PolymerElement) {
guid:
string;
apnProperties:
ApnProperties|undefined;
mode:
ApnDetailDialogMode;
apnList:
ApnProperties[];
}
declare global {
interface HTMLElementTagNameMap {
'apn-detail-dialog': ApnDetailDialog;
}
}

@ -91,6 +91,7 @@ export {LifetimeBrowserProxy, LifetimeBrowserProxyImpl} from '/shared/settings/l
export {ProfileInfo, ProfileInfoBrowserProxy, ProfileInfoBrowserProxyImpl} from '/shared/settings/people_page/profile_info_browser_proxy.js';
export {PageStatus, StatusAction, StoredAccount, SyncBrowserProxy, SyncBrowserProxyImpl, SyncPrefs, SyncStatus} from '/shared/settings/people_page/sync_browser_proxy.js';
export {PrivacyPageBrowserProxyImpl, SecureDnsMode, SecureDnsUiManagementMode} from '/shared/settings/privacy_page/privacy_page_browser_proxy.js';
export {ApnDetailDialog} from 'chrome://resources/ash/common/network/apn_detail_dialog.js';
export {AppManagementFileHandlingItemElement} from 'chrome://resources/cr_components/app_management/file_handling_item.js';
export {AppManagementSupportedLinksItemElement} from 'chrome://resources/cr_components/app_management/supported_links_item.js';
export {AppManagementToggleRowElement} from 'chrome://resources/cr_components/app_management/toggle_row.js';

@ -22,7 +22,7 @@ build_webui_tests("build") {
files = [
"apn_subpage_tests.js",
"apn_detail_dialog_tests.js",
"apn_detail_dialog_test.ts",
"cellular_networks_list_test.js",
"cellular_roaming_toggle_button_test.js",
"crostini_extra_containers_subpage_test.js",

@ -0,0 +1,520 @@
// Copyright 2022 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/os_settings.js';
import {ApnDetailDialog, CrCheckboxElement, CrDialogElement, CrInputElement} from 'chrome://os-settings/os_settings.js';
import {ApnDetailDialogMode} from 'chrome://resources/ash/common/network/cellular_utils.js';
import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
import {ApnAuthenticationType, ApnIpType, ApnProperties, ApnState, ApnType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {assertEquals, assertFalse, assertNull, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js';
import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
import {disableAnimationsAndTransitions} from 'chrome://webui-test/test_api.js';
import {eventToPromise} from 'chrome://webui-test/test_util.js';
const TEST_APN: ApnProperties = {
accessPointName: 'apn',
username: 'username',
password: 'password',
authentication: ApnAuthenticationType.kAutomatic,
ipType: ApnIpType.kAutomatic,
apnTypes: [ApnType.kDefault],
state: ApnState.kEnabled,
id: undefined,
language: undefined,
localizedName: undefined,
name: undefined,
attach: undefined,
};
suite('<apn-detail-dialog>', () => {
let apnDetailDialog: ApnDetailDialog;
let mojoApi: FakeNetworkConfig;
function toggleAdvancedSettings(): void {
const advancedSettingsBtn =
apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#advancedSettingsBtn');
assertTrue(!!advancedSettingsBtn);
advancedSettingsBtn.click();
}
function assertElementEnabled(selector: string): void {
const element = apnDetailDialog.shadowRoot!.querySelector<
HTMLInputElement|HTMLSelectElement|HTMLButtonElement|CrCheckboxElement>(
selector);
assertTrue(!!element);
assertFalse(element.disabled);
}
function assertAllInputsEnabled(): void {
assertElementEnabled('#apnInput');
assertElementEnabled('#usernameInput');
assertElementEnabled('#passwordInput');
assertElementEnabled('#authTypeDropDown');
assertElementEnabled('#apnDefaultTypeCheckbox');
assertElementEnabled('#apnAttachTypeCheckbox');
assertElementEnabled('#ipTypeDropDown');
}
suiteSetup(() => {
disableAnimationsAndTransitions();
mojoApi = new FakeNetworkConfig();
MojoInterfaceProviderImpl.getInstance().setMojoServiceRemoteForTest(
mojoApi);
});
teardown(() => {
apnDetailDialog.remove();
mojoApi.resetForTest();
});
async function init(
mode?: ApnDetailDialogMode,
apnProperties?: ApnProperties): Promise<void> {
apnDetailDialog = document.createElement('apn-detail-dialog');
apnDetailDialog.guid = 'fake-guid';
apnDetailDialog.apnList = [TEST_APN];
apnDetailDialog.mode = mode || ApnDetailDialogMode.CREATE;
apnDetailDialog.apnProperties = apnProperties;
document.body.appendChild(apnDetailDialog);
await waitAfterNextRender(apnDetailDialog);
}
test('Element contains dialog', async () => {
await init();
const dialog = apnDetailDialog.shadowRoot!.querySelector('cr-dialog');
assertTrue(!!dialog);
assertTrue(dialog.open);
// Confirm that the dialog has the add apn title.
const apnDetailDialogTitle =
apnDetailDialog.shadowRoot!.querySelector<HTMLElement>(
'#apnDetailDialogTitle');
assertTrue(!!apnDetailDialogTitle);
assertEquals(
apnDetailDialog.i18n('apnDetailAddApnDialogTitle'),
apnDetailDialogTitle.innerText);
assertTrue(!!apnDetailDialog.shadowRoot!.querySelector('#apnInput'));
assertTrue(!!apnDetailDialog.shadowRoot!.querySelector('#usernameInput'));
assertTrue(!!apnDetailDialog.shadowRoot!.querySelector('#passwordInput'));
assertTrue(
!!apnDetailDialog.shadowRoot!.querySelector('#authTypeDropDown'));
const defaultTypeCheckbox =
apnDetailDialog.shadowRoot!.querySelector<CrCheckboxElement>(
'#apnDefaultTypeCheckbox');
assertTrue(!!defaultTypeCheckbox);
assertTrue(defaultTypeCheckbox.checked);
assertTrue(
!!apnDetailDialog.shadowRoot!.querySelector('#apnAttachTypeCheckbox'));
assertTrue(!!apnDetailDialog.shadowRoot!.querySelector('#ipTypeDropDown'));
assertTrue(
!!apnDetailDialog.shadowRoot!.querySelector('#apnDetailCancelBtn'));
assertTrue(
!!apnDetailDialog.shadowRoot!.querySelector('#apnDetailActionBtn'));
assertNull(apnDetailDialog.shadowRoot!.querySelector('#apnDoneBtn'));
assertEquals(
apnDetailDialog.shadowRoot!.querySelector('#apnInput'),
apnDetailDialog.shadowRoot!.activeElement);
});
test('Clicking the cancel button fires the close event', async () => {
await init();
const closeEventPromise = eventToPromise('close', window);
const cancelBtn =
apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDetailCancelBtn');
assertTrue(!!cancelBtn);
cancelBtn.click();
await closeEventPromise;
const crDialogElement =
apnDetailDialog.shadowRoot!.querySelector<CrDialogElement>(
'#apnDetailDialog');
assertTrue(!!crDialogElement);
assertFalse(crDialogElement.open);
});
test(
'Clicking on the advanced settings button expands/collapses section',
async () => {
await init();
const isAdvancedSettingShowing = () => {
const ironCollapseElement =
apnDetailDialog.shadowRoot!.querySelector('iron-collapse');
assertTrue(!!ironCollapseElement);
return ironCollapseElement.opened;
};
assertFalse(isAdvancedSettingShowing());
toggleAdvancedSettings();
assertTrue(!!isAdvancedSettingShowing());
toggleAdvancedSettings();
assertFalse(isAdvancedSettingShowing());
toggleAdvancedSettings();
const assertOptions =
(expectedTextArray: string[],
optionNodes: NodeListOf<HTMLOptionElement>) => {
for (const [idx, expectedText] of expectedTextArray.entries()) {
assertTrue(!!optionNodes[idx]);
assertTrue(!!optionNodes[idx]!.text);
assertEquals(expectedText, optionNodes[idx]!.text);
}
};
const authTypeDropDown =
apnDetailDialog.shadowRoot!.querySelector('#authTypeDropDown');
assertTrue(!!authTypeDropDown);
const authTypeOptionNodes = authTypeDropDown.querySelectorAll('option');
assertEquals(3, authTypeOptionNodes.length);
// Note: We are also checking that the items appear in a certain order.
assertOptions(
[
apnDetailDialog.i18n('apnDetailTypeAuto'),
apnDetailDialog.i18n('apnDetailAuthTypePAP'),
apnDetailDialog.i18n('apnDetailAuthTypeCHAP'),
],
authTypeOptionNodes);
const ipTypeDropDown =
apnDetailDialog.shadowRoot!.querySelector('#ipTypeDropDown');
assertTrue(!!ipTypeDropDown);
const ipTypeOptionNodes =
ipTypeDropDown.querySelectorAll<HTMLOptionElement>('option');
assertEquals(4, ipTypeOptionNodes.length);
assertOptions(
[
apnDetailDialog.i18n('apnDetailTypeAuto'),
apnDetailDialog.i18n('apnDetailIpTypeIpv4'),
apnDetailDialog.i18n('apnDetailIpTypeIpv6'),
apnDetailDialog.i18n('apnDetailIpTypeIpv4_Ipv6'),
],
ipTypeOptionNodes);
});
test('Clicking on the add button calls createCustomApn', async () => {
await init();
const apnInput =
apnDetailDialog.shadowRoot!.querySelector<HTMLInputElement>(
'#apnInput');
assertTrue(!!apnInput);
apnInput.value = TEST_APN.accessPointName;
const usernameInput =
apnDetailDialog.shadowRoot!.querySelector<HTMLInputElement>(
'#usernameInput');
assertTrue(!!usernameInput);
usernameInput.value = TEST_APN.username!;
const passwordInput =
apnDetailDialog.shadowRoot!.querySelector<HTMLInputElement>(
'#passwordInput');
assertTrue(!!passwordInput);
passwordInput.value = TEST_APN.password!;
assertAllInputsEnabled();
assertElementEnabled('#apnDetailCancelBtn');
let actionBtn =
apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDetailActionBtn');
assertTrue(!!actionBtn);
assertEquals(apnDetailDialog.i18n('add'), actionBtn.innerText);
// Add a network.
const network = OncMojo.getDefaultManagedProperties(
NetworkType.kCellular, apnDetailDialog.guid, apnDetailDialog.guid);
mojoApi.setManagedPropertiesForTest(network);
await flushTasks();
const properties = await mojoApi.getManagedProperties(apnDetailDialog.guid);
assertTrue(!!properties);
assertEquals(
undefined, properties.result.typeProperties.cellular!.customApnList);
actionBtn = apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDetailActionBtn');
assertTrue(!!actionBtn);
actionBtn.click();
await flushTasks();
await mojoApi.whenCalled('createCustomApn');
assertEquals(
1, properties.result.typeProperties.cellular!.customApnList!.length);
const apn = properties.result.typeProperties.cellular!.customApnList![0];
assertTrue(!!apn);
assertEquals(TEST_APN.accessPointName, apn.accessPointName);
assertEquals(TEST_APN.username, apn.username);
assertEquals(TEST_APN.password, apn.password);
assertEquals(TEST_APN.authentication, apn.authentication);
assertEquals(TEST_APN.ipType, apn.ipType);
assertEquals(TEST_APN.apnTypes.length, apn.apnTypes.length);
assertEquals(TEST_APN.apnTypes[0], apn.apnTypes[0]);
});
test('Setting mode to view changes buttons and fields', async () => {
const assertFieldDisabled = (selector: string) => {
const element = apnDetailDialog.shadowRoot!.querySelector<
HTMLInputElement|HTMLSelectElement|CrCheckboxElement>(selector);
assertTrue(!!element);
assertTrue(element.disabled);
};
// Set the dialog mode before opening the dialog so that the default focus
// can be tested.
await init(
/* mode= */ ApnDetailDialogMode.VIEW, /* apnProperties= */ TEST_APN);
const apnDetailDialogTitle =
apnDetailDialog.shadowRoot!.querySelector<HTMLElement>(
'#apnDetailDialogTitle');
assertTrue(!!apnDetailDialogTitle);
assertEquals(
apnDetailDialog.i18n('apnDetailViewApnDialogTitle'),
apnDetailDialogTitle.innerText);
assertNull(
apnDetailDialog.shadowRoot!.querySelector('#apnDetailCancelBtn'));
assertNull(
apnDetailDialog.shadowRoot!.querySelector('#apnDetailActionBtn'));
const doneBtn =
apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDoneBtn');
assertTrue(!!doneBtn);
assertFalse(doneBtn.disabled);
assertFieldDisabled('#apnInput');
assertFieldDisabled('#usernameInput');
assertFieldDisabled('#passwordInput');
assertFieldDisabled('#authTypeDropDown');
assertFieldDisabled('#apnDefaultTypeCheckbox');
assertFieldDisabled('#apnAttachTypeCheckbox');
assertFieldDisabled('#ipTypeDropDown');
assertEquals(doneBtn, apnDetailDialog.shadowRoot!.activeElement);
});
test('Dialog input fields are validated', async () => {
await init();
const apnInputField =
apnDetailDialog.shadowRoot!.querySelector<CrInputElement>('#apnInput');
assertTrue(!!apnInputField);
const actionButton =
apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDetailActionBtn');
assertTrue(!!actionButton);
// Case: After opening dialog before user input
assertFalse(apnInputField.invalid);
assertTrue(actionButton.disabled);
// Case : After valid user input
apnInputField.value = 'test';
assertFalse(apnInputField.invalid);
assertFalse(actionButton.disabled);
// Case : After Removing all user input no error state but button disabled
apnInputField.value = '';
assertFalse(apnInputField.invalid);
assertTrue(actionButton.disabled);
// Case : Non ascii user input
apnInputField.value = 'testμ';
assertTrue(apnInputField.invalid);
assertTrue(actionButton.disabled);
assertStringContains(apnInputField.value, 'μ');
// Case : longer than 63 characters then removing one character
apnInputField.value = 'a'.repeat(64);
assertTrue(apnInputField.invalid);
assertTrue(actionButton.disabled);
assertEquals(63, apnInputField.value.length);
apnInputField.value = apnInputField.value.slice(0, -1);
assertFalse(apnInputField.invalid);
assertFalse(actionButton.disabled);
// Case : longer than 63 non-ASCII characters
apnInputField.value = 'μ'.repeat(64);
assertTrue(apnInputField.invalid);
assertTrue(actionButton.disabled);
});
test('Apn types are correctly validated in all modes', async () => {
await init();
const updateApnTypeCheckboxes =
(defaultType: boolean, attachType: boolean) => {
const apnDefaultTypeCheckbox =
apnDetailDialog.shadowRoot!.querySelector<CrCheckboxElement>(
'#apnDefaultTypeCheckbox');
assertTrue(!!apnDefaultTypeCheckbox);
apnDefaultTypeCheckbox.checked = defaultType;
const apnAttachTypeCheckbox =
apnDetailDialog.shadowRoot!.querySelector<CrCheckboxElement>(
'#apnAttachTypeCheckbox');
assertTrue(!!apnAttachTypeCheckbox);
apnAttachTypeCheckbox.checked = attachType;
};
toggleAdvancedSettings();
const getDefaultApnInfo = () =>
apnDetailDialog.shadowRoot!.querySelector('#defaultApnRequiredInfo');
TEST_APN.id = '1';
const currentApn = {...TEST_APN};
currentApn.id = '2';
apnDetailDialog.set('apnList', [TEST_APN, currentApn]);
apnDetailDialog.set('apnProperties', currentApn);
const actionButton =
apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDetailActionBtn');
assertTrue(!!actionButton);
const apnInputField =
apnDetailDialog.shadowRoot!.querySelector<HTMLInputElement>(
'#apnInput');
assertTrue(!!apnInputField);
apnInputField.value = 'valid_name';
// CREATE mode tests
apnDetailDialog.mode = ApnDetailDialogMode.CREATE;
TEST_APN.state = ApnState.kDisabled;
apnDetailDialog.set('apnList', [TEST_APN]);
// Case: Default APN type is checked
updateApnTypeCheckboxes(/* default= */ true, /* attach= */ false);
await flushTasks();
assertFalse(actionButton.disabled);
assertNull(getDefaultApnInfo());
// Case: No enabled default APNs, default unchecked and attach is checked.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
// Case: No enabled default APNs and both unchecked.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertNull(getDefaultApnInfo());
// Case: Enabled default APNs, default unchecked and attach is checked.
TEST_APN.state = ApnState.kEnabled;
apnDetailDialog.set('apnList', [TEST_APN]);
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertFalse(actionButton.disabled);
assertNull(getDefaultApnInfo());
// Case: Enabled default APNs and both unchecked.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertNull(getDefaultApnInfo());
// Edit mode tests
apnDetailDialog.set('mode', ApnDetailDialogMode.EDIT);
TEST_APN.apnTypes = [ApnType.kAttach];
currentApn.apnTypes = [ApnType.kDefault, ApnType.kAttach];
apnDetailDialog.set('apnList', [TEST_APN, currentApn]);
// Case: Default APN type is checked
updateApnTypeCheckboxes(/* default= */ true, /* attach= */ false);
await flushTasks();
assertFalse(actionButton.disabled);
assertNull(getDefaultApnInfo());
// Case: User unchecks the default checkbox, APN being modified is the
// only default APN
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
// Case: User unchecks both checkboxes, APN being modified is the
// only enabled default APN but there are other enabled attach APNs.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
// Case: User unchecks both checkboxes, APN being modified is the
// only enabled default APN and is the only enabled attachApn.
currentApn.apnTypes = [ApnType.kDefault, ApnType.kAttach];
apnDetailDialog.set('apnList', [currentApn]);
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertNull(getDefaultApnInfo());
// Case: User unchecks default APN type checkbox and checks the attach
// APN type checkbox, APN being modified is the only enabled default APN
// and there are no other enabled attach type APNs.
currentApn.apnTypes = [ApnType.kDefault];
apnDetailDialog.set('apnList', [currentApn]);
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
});
test('Setting mode to edit changes buttons and fields', async () => {
const apnWithId = TEST_APN;
apnWithId.id = '1';
apnWithId.apnTypes = [ApnType.kDefault];
// Set the dialog mode before opening the dialog so that the default focus
// can be tested.
await init(
/* mode= */ ApnDetailDialogMode.EDIT, /* apnProperties= */ apnWithId);
const apnDetailDialogTitle =
apnDetailDialog.shadowRoot!.querySelector<HTMLElement>(
'#apnDetailDialogTitle');
assertTrue(!!apnDetailDialogTitle);
assertEquals(
apnDetailDialog.i18n('apnDetailEditApnDialogTitle'),
apnDetailDialogTitle.innerText);
assertElementEnabled('#apnDetailCancelBtn');
let button = apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDetailActionBtn');
assertTrue(!!button);
assertEquals(apnDetailDialog.i18n('save'), button.innerText);
assertNull(apnDetailDialog.shadowRoot!.querySelector('#apnDoneBtn'));
assertAllInputsEnabled();
// Case: clicking on the action button calls the correct method
const network = OncMojo.getDefaultManagedProperties(
NetworkType.kCellular, apnDetailDialog.guid, apnDetailDialog.guid);
mojoApi.setManagedPropertiesForTest(network);
await flushTasks();
const managedProperties =
await mojoApi.getManagedProperties(apnDetailDialog.guid);
assertTrue(!!managedProperties);
mojoApi.createCustomApn(apnDetailDialog.guid, apnWithId);
const newExpectedApn = 'modified';
const apnInputField =
apnDetailDialog.shadowRoot!.querySelector<HTMLInputElement>(
'#apnInput');
assertTrue(!!apnInputField);
apnInputField.value = newExpectedApn;
button = apnDetailDialog.shadowRoot!.querySelector<HTMLButtonElement>(
'#apnDetailActionBtn');
assertTrue(!!button);
button.click();
await mojoApi.whenCalled('modifyCustomApn');
const apn =
managedProperties.result.typeProperties.cellular!.customApnList![0];
assertTrue(!!apn);
assertEquals(newExpectedApn, apn.accessPointName);
assertEquals(apnWithId.id, apn.id);
assertEquals(apnWithId.username, apn.username);
assertEquals(apnWithId.password, apn.password);
assertEquals(apnWithId.authentication, apn.authentication);
assertEquals(apnWithId.ipType, apn.ipType);
assertEquals(apnWithId.apnTypes.length, apn.apnTypes.length);
assertEquals(apnWithId.apnTypes[0], apn.apnTypes[0]);
assertEquals(
apnDetailDialog.shadowRoot!.querySelector('#apnInput'),
apnDetailDialog.shadowRoot!.activeElement);
});
});

@ -1,461 +0,0 @@
// Copyright 2022 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/strings.m.js';
import 'chrome://resources/ash/common/network/apn_detail_dialog.js';
import {ApnDetailDialog} from 'chrome://resources/ash/common/network/apn_detail_dialog.js';
import {ApnDetailDialogMode} from 'chrome://resources/ash/common/network/cellular_utils.js';
import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
import {ApnAuthenticationType, ApnIpType, ApnProperties, ApnState, ApnType, ManagedProperties} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
import {eventToPromise} from 'chrome://webui-test/test_util.js';
/** @type {!ApnProperties} */
const TEST_APN = {
accessPointName: 'apn',
username: 'username',
password: 'password',
authentication: ApnAuthenticationType.kAutomatic,
ipType: ApnIpType.kAutomatic,
apnTypes: [ApnType.kDefault],
state: ApnState.kEnabled,
};
suite('ApnDetailDialog', function() {
/** @type {ApnDetailDialog} */
let apnDetailDialog = null;
let mojoApi_ = null;
async function toggleAdvancedSettings() {
const advancedSettingsBtn =
apnDetailDialog.shadowRoot.querySelector('#advancedSettingsBtn');
assertTrue(!!advancedSettingsBtn);
advancedSettingsBtn.click();
}
/**
* @param {string} selector
*/
function assertElementEnabled(selector) {
const element = apnDetailDialog.shadowRoot.querySelector(selector);
assertTrue(!!element);
assertFalse(element.disabled);
}
function assertAllInputsEnabled() {
assertElementEnabled('#apnInput');
assertElementEnabled('#usernameInput');
assertElementEnabled('#passwordInput');
assertElementEnabled('#authTypeDropDown');
assertElementEnabled('#apnDefaultTypeCheckbox');
assertElementEnabled('#apnAttachTypeCheckbox');
assertElementEnabled('#ipTypeDropDown');
}
setup(async function() {
testing.Test.disableAnimationsAndTransitions();
PolymerTest.clearBody();
mojoApi_ = new FakeNetworkConfig();
MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
apnDetailDialog = document.createElement('apn-detail-dialog');
apnDetailDialog.guid = 'fake-guid';
apnDetailDialog.apnList = [TEST_APN];
});
teardown(function() {
return flushTasks().then(() => {
apnDetailDialog.remove();
apnDetailDialog = null;
});
});
async function init() {
document.body.appendChild(apnDetailDialog);
return waitAfterNextRender(apnDetailDialog);
}
test('Element contains dialog', async function() {
await init();
const dialog = apnDetailDialog.shadowRoot.querySelector('cr-dialog');
assertTrue(!!dialog);
assertTrue(dialog.open);
// Confirm that the dialog has the add apn title.
assertEquals(
apnDetailDialog.i18n('apnDetailAddApnDialogTitle'),
apnDetailDialog.shadowRoot.querySelector('#apnDetailDialogTitle')
.innerText);
assertTrue(!!apnDetailDialog.shadowRoot.querySelector('#apnInput'));
assertTrue(!!apnDetailDialog.shadowRoot.querySelector('#usernameInput'));
assertTrue(!!apnDetailDialog.shadowRoot.querySelector('#passwordInput'));
assertTrue(!!apnDetailDialog.shadowRoot.querySelector('#authTypeDropDown'));
const defaultTypeCheckbox =
apnDetailDialog.shadowRoot.querySelector('#apnDefaultTypeCheckbox');
assertTrue(!!defaultTypeCheckbox);
assertTrue(defaultTypeCheckbox.checked);
assertTrue(
!!apnDetailDialog.shadowRoot.querySelector('#apnAttachTypeCheckbox'));
assertTrue(!!apnDetailDialog.shadowRoot.querySelector('#ipTypeDropDown'));
assertTrue(
!!apnDetailDialog.shadowRoot.querySelector('#apnDetailCancelBtn'));
assertTrue(
!!apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn'));
assertFalse(!!apnDetailDialog.shadowRoot.querySelector('#apnDoneBtn'));
assertEquals(
apnDetailDialog.shadowRoot.querySelector('#apnInput'),
apnDetailDialog.shadowRoot.activeElement);
});
test('Clicking the cancel button fires the close event', async function() {
await init();
const closeEventPromise = eventToPromise('close', window);
const cancelBtn =
apnDetailDialog.shadowRoot.querySelector('#apnDetailCancelBtn');
assertTrue(!!cancelBtn);
cancelBtn.click();
await closeEventPromise;
assertFalse(!!apnDetailDialog.$.apnDetailDialog.open);
});
test(
'Clicking on the advanced settings button expands/collapses section',
async function() {
await init();
const isAdvancedSettingShowing = () =>
apnDetailDialog.shadowRoot.querySelector('iron-collapse').opened;
assertFalse(!!isAdvancedSettingShowing());
toggleAdvancedSettings();
assertTrue(!!isAdvancedSettingShowing());
toggleAdvancedSettings();
assertFalse(!!isAdvancedSettingShowing());
toggleAdvancedSettings();
const assertOptions = (expectedTextArray, optionNodes) => {
for (const [idx, expectedText] of expectedTextArray.entries()) {
assertTrue(!!optionNodes[idx]);
assertTrue(!!optionNodes[idx].text);
assertEquals(expectedText, optionNodes[idx].text);
}
};
const authTypeDropDown =
apnDetailDialog.shadowRoot.querySelector('#authTypeDropDown');
assertTrue(!!authTypeDropDown);
const authTypeOptionNodes = authTypeDropDown.querySelectorAll('option');
assertEquals(3, authTypeOptionNodes.length);
// Note: We are also checking that the items appear in a certain order.
assertOptions(
[
apnDetailDialog.i18n('apnDetailTypeAuto'),
apnDetailDialog.i18n('apnDetailAuthTypePAP'),
apnDetailDialog.i18n('apnDetailAuthTypeCHAP'),
],
authTypeOptionNodes);
const ipTypeDropDown =
apnDetailDialog.shadowRoot.querySelector('#ipTypeDropDown');
assertTrue(!!ipTypeDropDown);
const ipTypeOptionNodes = ipTypeDropDown.querySelectorAll('option');
assertEquals(4, ipTypeOptionNodes.length);
assertOptions(
[
apnDetailDialog.i18n('apnDetailTypeAuto'),
apnDetailDialog.i18n('apnDetailIpTypeIpv4'),
apnDetailDialog.i18n('apnDetailIpTypeIpv6'),
apnDetailDialog.i18n('apnDetailIpTypeIpv4_Ipv6'),
],
ipTypeOptionNodes);
});
test('Clicking on the add button calls createCustomApn', async () => {
await init();
apnDetailDialog.$.apnInput.value = TEST_APN.accessPointName;
apnDetailDialog.$.usernameInput.value = TEST_APN.username;
apnDetailDialog.$.passwordInput.value = TEST_APN.password;
assertAllInputsEnabled();
assertElementEnabled('#apnDetailCancelBtn');
assertElementEnabled('#apnDetailActionBtn');
assertEquals(
apnDetailDialog.i18n('add'),
apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn')
.innerText);
// Add a network.
const network = OncMojo.getDefaultManagedProperties(
NetworkType.kCellular, apnDetailDialog.guid);
mojoApi_.setManagedPropertiesForTest(network);
await flushTasks();
/**
* @type {!ManagedProperties}
*/
const properties =
await mojoApi_.getManagedProperties(apnDetailDialog.guid);
assertTrue(!!properties);
assertFalse(!!properties.result.typeProperties.cellular.customApnList);
const actionBtn =
apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn');
assertTrue(!!actionBtn);
actionBtn.click();
await flushTasks();
await mojoApi_.whenCalled('createCustomApn');
assertEquals(
1, properties.result.typeProperties.cellular.customApnList.length);
const apn = properties.result.typeProperties.cellular.customApnList[0];
assertEquals(TEST_APN.accessPointName, apn.accessPointName);
assertEquals(TEST_APN.username, apn.username);
assertEquals(TEST_APN.password, apn.password);
assertEquals(TEST_APN.authentication, apn.authentication);
assertEquals(TEST_APN.ipType, apn.ipType);
assertEquals(TEST_APN.apnTypes.length, apn.apnTypes.length);
assertEquals(TEST_APN.apnTypes[0], apn.apnTypes[0]);
});
test('Setting mode to view changes buttons and fields', async () => {
const assertFieldDisabled = (selector) => {
const element = apnDetailDialog.shadowRoot.querySelector(selector);
assertTrue(!!element);
assertTrue(element.disabled);
};
// Set the dialog mode before opening the dialog so that the default focus
// can be tested.
apnDetailDialog.mode = ApnDetailDialogMode.VIEW;
apnDetailDialog.apnProperties = TEST_APN;
await init();
assertEquals(
apnDetailDialog.i18n('apnDetailViewApnDialogTitle'),
apnDetailDialog.shadowRoot.querySelector('#apnDetailDialogTitle')
.innerText);
assertFalse(
!!apnDetailDialog.shadowRoot.querySelector('#apnDetailCancelBtn'));
assertFalse(
!!apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn'));
const doneBtn = apnDetailDialog.shadowRoot.querySelector('#apnDoneBtn');
assertTrue(!!doneBtn);
assertFalse(doneBtn.disabled);
assertFieldDisabled('#apnInput');
assertFieldDisabled('#usernameInput');
assertFieldDisabled('#passwordInput');
assertFieldDisabled('#authTypeDropDown');
assertFieldDisabled('#apnDefaultTypeCheckbox');
assertFieldDisabled('#apnAttachTypeCheckbox');
assertFieldDisabled('#ipTypeDropDown');
assertEquals(doneBtn, apnDetailDialog.shadowRoot.activeElement);
});
test('Dialog input fields are validated', async () => {
await init();
const apnInputField = apnDetailDialog.$.apnInput;
const actionButton =
apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn');
assertTrue(!!actionButton);
// Case: After opening dialog before user input
assertFalse(!!apnInputField.invalid);
assertTrue(!!actionButton.disabled);
// Case : After valid user input
apnInputField.value = 'test';
assertFalse(!!apnInputField.invalid);
assertFalse(!!actionButton.disabled);
// Case : After Removing all user input no error state but button disabled
apnInputField.value = '';
assertFalse(!!apnInputField.invalid);
assertTrue(!!actionButton.disabled);
// Case : Non ascii user input
apnInputField.value = 'testμ';
assertTrue(!!apnInputField.invalid);
assertTrue(!!actionButton.disabled);
assertTrue(apnInputField.value.includes('μ'));
// Case : longer than 63 characters then removing one character
apnInputField.value = 'a'.repeat(64);
assertTrue(!!apnInputField.invalid);
assertTrue(!!actionButton.disabled);
assertFalse(apnInputField.value.length > 63);
apnInputField.value = apnInputField.value.slice(0, -1);
assertFalse(!!apnInputField.invalid);
assertFalse(!!actionButton.disabled);
// Case : longer than 63 non-ASCII characters
apnInputField.value = 'μ'.repeat(64);
assertTrue(!!apnInputField.invalid);
assertTrue(!!actionButton.disabled);
});
test('Apn types are correctly validated in all modes', async () => {
await init();
const updateApnTypeCheckboxes = (defaultType, attachType) => {
apnDetailDialog.$.apnDefaultTypeCheckbox.checked = defaultType;
apnDetailDialog.$.apnAttachTypeCheckbox.checked = attachType;
};
await toggleAdvancedSettings();
const getDefaultApnInfo = () => {
return apnDetailDialog.shadowRoot.querySelector(
'#defaultApnRequiredInfo');
};
TEST_APN.id = '1';
const currentApn = /** @type {ApnProperties} */ ({});
Object.assign(currentApn, TEST_APN);
currentApn.id = '2';
apnDetailDialog.apnList = [TEST_APN, currentApn];
apnDetailDialog.apnProperties = currentApn;
const actionButton =
apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn');
apnDetailDialog.$.apnInput.value = 'valid_name';
// CREATE mode tests
apnDetailDialog.mode = ApnDetailDialogMode.CREATE;
TEST_APN.state = ApnState.kDisabled;
apnDetailDialog.apnList = [TEST_APN];
// Case: Default APN type is checked
updateApnTypeCheckboxes(/* default= */ true, /* attach= */ false);
await flushTasks();
assertFalse(actionButton.disabled);
assertFalse(!!getDefaultApnInfo());
// Case: No enabled default APNs, default unchecked and attach is checked.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
// Case: No enabled default APNs and both unchecked.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertFalse(!!getDefaultApnInfo());
// Case: Enabled default APNs, default unchecked and attach is checked.
TEST_APN.state = ApnState.kEnabled;
apnDetailDialog.apnList = [TEST_APN];
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertFalse(actionButton.disabled);
assertFalse(!!getDefaultApnInfo());
// Case: Enabled default APNs and both unchecked.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertFalse(!!getDefaultApnInfo());
// Edit mode tests
apnDetailDialog.mode = ApnDetailDialogMode.EDIT;
TEST_APN.apnTypes = [ApnType.kAttach];
currentApn.apnTypes = [ApnType.kDefault, ApnType.kAttach];
apnDetailDialog.apnList = [TEST_APN, currentApn];
// Case: Default APN type is checked
updateApnTypeCheckboxes(/* default= */ true, /* attach= */ false);
await flushTasks();
assertFalse(actionButton.disabled);
assertFalse(!!getDefaultApnInfo());
// Case: User unchecks the default checkbox, APN being modified is the
// only default APN
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
// Case: User unchecks both checkboxes, APN being modified is the
// only enabled default APN but there are other enabled attach APNs.
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
// Case: User unchecks both checkboxes, APN being modified is the
// only enabled default APN and is the only enabled attachApn.
currentApn.apnTypes = [ApnType.kDefault, ApnType.kAttach];
apnDetailDialog.apnList = [currentApn];
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ false);
await flushTasks();
assertTrue(actionButton.disabled);
assertFalse(!!getDefaultApnInfo());
// Case: User unchecks default APN type checkbox and checks the attach
// APN type checkbox, APN being modified is the only enabled default APN
// and there are no other enabled attach type APNs.
currentApn.apnTypes = [ApnType.kDefault];
apnDetailDialog.apnList = [currentApn];
updateApnTypeCheckboxes(/* default= */ false, /* attach= */ true);
await flushTasks();
assertTrue(actionButton.disabled);
assertTrue(!!getDefaultApnInfo());
});
test('Setting mode to edit changes buttons and fields', async () => {
const apnWithId = TEST_APN;
apnWithId.id = '1';
apnWithId.apnTypes = [ApnType.kDefault];
// Set the dialog mode before opening the dialog so that the default focus
// can be tested.
apnDetailDialog.mode = ApnDetailDialogMode.EDIT;
apnDetailDialog.apnProperties = apnWithId;
await init();
assertEquals(
apnDetailDialog.i18n('apnDetailEditApnDialogTitle'),
apnDetailDialog.shadowRoot.querySelector('#apnDetailDialogTitle')
.innerText);
assertElementEnabled('#apnDetailCancelBtn');
assertElementEnabled('#apnDetailActionBtn');
assertEquals(
apnDetailDialog.i18n('save'),
apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn')
.innerText);
assertFalse(!!apnDetailDialog.shadowRoot.querySelector('#apnDoneBtn'));
assertAllInputsEnabled();
// Case: clicking on the action button calls the correct method
const network = OncMojo.getDefaultManagedProperties(
NetworkType.kCellular, apnDetailDialog.guid);
mojoApi_.setManagedPropertiesForTest(network);
await flushTasks();
const managedProperties =
await mojoApi_.getManagedProperties(apnDetailDialog.guid);
assertTrue(!!managedProperties);
managedProperties.result.typeProperties.cellular = {
customApnList: [apnWithId],
};
const newExpectedApn = 'modified';
apnDetailDialog.$.apnInput.value = newExpectedApn;
apnDetailDialog.shadowRoot.querySelector('#apnDetailActionBtn').click();
await mojoApi_.whenCalled('modifyCustomApn');
const apn =
managedProperties.result.typeProperties.cellular.customApnList[0];
assertEquals(newExpectedApn, apn.accessPointName);
assertEquals(apnWithId.id, apn.id);
assertEquals(apnWithId.username, apn.username);
assertEquals(apnWithId.password, apn.password);
assertEquals(apnWithId.authentication, apn.authentication);
assertEquals(apnWithId.ipType, apn.ipType);
assertEquals(apnWithId.apnTypes.length, apn.apnTypes.length);
assertEquals(apnWithId.apnTypes[0], apn.apnTypes[0]);
assertEquals(
apnDetailDialog.shadowRoot.querySelector('#apnInput'),
apnDetailDialog.shadowRoot.activeElement);
});
});

@ -205,7 +205,7 @@ TEST_F('OSSettingsCrostiniExtraContainerPageTest', 'AllJsTests', () => {
});
[['AboutPage', 'os_about_page_tests.js'],
['ApnDetailDialog', 'apn_detail_dialog_tests.js'],
['ApnDetailDialog', 'apn_detail_dialog_test.js'],
// TODO(crbug.com/1455866): Enable the ApnSubpage test.
// [
// 'ApnSubpage', 'apn_subpage_tests.js',