chromium/chrome/test/data/webui/chromeos/settings/os_languages_page/app_languages_page_test.ts

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

import 'chrome://os-settings/lazy_load.js';

import {OsSettingsAppLanguagesPageElement} from 'chrome://os-settings/lazy_load.js';
import {AppManagementStore} from 'chrome://os-settings/os_settings.js';
import {App, AppType} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
import {assertEquals, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';

import {FakePageHandler} from '../app_management/fake_page_handler.js';
import {replaceBody, replaceStore, setupFakeHandler} from '../app_management/test_util.js';

type AppConfig = Partial<App>;

suite('<app-languages-page>', () => {
  const crIconButtonTag = 'cr-icon-button';
  const deviceLanguageLocaleTag = '';
  const deviceLanguageLabel = 'Device language';
  let appLanguagesPage: OsSettingsAppLanguagesPageElement;
  let fakeHandler: FakePageHandler;

  setup(() => {
    fakeHandler = setupFakeHandler();
    replaceStore();

    appLanguagesPage = document.createElement('os-settings-app-languages-page');
    appLanguagesPage.prefs = {
      arc: {
        last_set_app_locale: {
          key: 'arc.last_set_app_locale',
          type: chrome.settingsPrivate.PrefType.STRING,
          value: '',
        },
      },
    };

    replaceBody(appLanguagesPage);
    flushTasks();
  });

  teardown(() => {
    appLanguagesPage.remove();
  });

  async function addApp(appId: string, arcOptions: AppConfig): Promise<void> {
    await fakeHandler.addApp(appId, arcOptions);
    await fakeHandler.flushPipesForTesting();
    flushTasks();
  }

  function getAppItems(): NodeListOf<HTMLDivElement> {
    return appLanguagesPage.shadowRoot!.querySelectorAll<HTMLDivElement>(
        '#appItem');
  }

  function getAppLanguagesDescriptionText(): string {
    const descriptionText =
        appLanguagesPage.shadowRoot!.querySelector<HTMLDivElement>(
            '#appLanguagesDescription');
    assertTrue(!!descriptionText, 'descriptionText not found');
    assertTrue(
        !!descriptionText.textContent, 'description textContent not found');
    return descriptionText.textContent;
  }

  function assertAppItem(
      list: NodeListOf<HTMLDivElement>, idx: number, title: string,
      selectedLanguage: string): void {
    const tag = `[appItem#${idx}]`;
    assertTrue(
        idx < list.length,
        `${tag} Invalid idx ${idx} is larger than list size ${list.length}`);
    const appItem = list[idx]!.querySelector('.app-info');
    assertTrue(!!appItem, `${tag} App item not found`);
    assertTrue(!!appItem.textContent, `${tag} Item textContent not found`);
    assertTrue(
        appItem.textContent.includes(title),
        `${tag} Invalid text ${appItem.textContent}`);

    const appLanguage = appItem.querySelector('.secondary');
    assertTrue(!!appLanguage, `${tag} App language not found`);
    assertTrue(
        !!appLanguage.textContent, `${tag} App language textContent not found`);
    assertTrue(
        appLanguage.textContent.includes(selectedLanguage),
        `${tag} Invalid text ${appLanguage.textContent}`);
  }

  test(
      'No apps with supported locales, show no-apps description text',
      async () => {
        const arcOptions: AppConfig = {
          type: AppType.kArc,
        };
        await addApp('no-supported-locales', arcOptions);

        assertStringContains(
            getAppLanguagesDescriptionText(),
            'No apps support app language selection');
      });

  test(
      'Multiple apps with supported locales, show supported apps, and ' +
          'description text',
      async () => {
        // Add app with selected locale.
        const testApp1Title = 'app1';
        const testDisplayName = 'testDisplayName';
        const arcOptions: AppConfig = {
          type: AppType.kArc,
          title: testApp1Title,
          supportedLocales: [{
            localeTag: 'test',
            displayName: testDisplayName,
            nativeDisplayName: '',
          }],
          selectedLocale: {
            localeTag: 'test',
            displayName: testDisplayName,
            nativeDisplayName: '',
          },
        };
        await addApp('supported-locales-with-selected-locale', arcOptions);

        // Add app with no selected locale (device language selected).
        const testApp2Title = 'app2';
        const arcOptions2: AppConfig = {
          type: AppType.kArc,
          title: testApp2Title,
          supportedLocales: [{
            localeTag: 'test',
            displayName: testDisplayName,
            nativeDisplayName: '',
          }],
          selectedLocale: {
            localeTag: '',
            displayName: '',
            nativeDisplayName: '',
          },
        };
        await addApp('supported-locales-without-selected-locale', arcOptions2);

        const appItems = getAppItems();
        assertEquals(2, appItems.length);
        assertAppItem(appItems, /* idx= */ 0, testApp1Title, testDisplayName);
        assertAppItem(
            appItems, /* idx= */ 1, testApp2Title, deviceLanguageLabel);

        assertStringContains(
            getAppLanguagesDescriptionText(),
            'Only apps that support language selection are shown here');
      });

  test(
      'Click three-dots button and edit language, show edit language dialog',
      async () => {
        const testAppTitle = 'testAppTitle';
        const arcOptions: AppConfig = {
          type: AppType.kArc,
          title: testAppTitle,
          supportedLocales: [{
            localeTag: 'test',
            displayName: '',
            nativeDisplayName: '',
          }],
          selectedLocale: {
            localeTag: '',
            displayName: '',
            nativeDisplayName: '',
          },
        };
        await addApp('click-three-dots-button-and-edit-language', arcOptions);

        // Clicks three-dots button to open dropdown menu.
        const appItems = getAppItems();
        assertEquals(1, appItems.length);
        const threeDotsButton = appItems[0]!.querySelector(crIconButtonTag);
        assertTrue(!!threeDotsButton, 'threeDotsButton not found');
        threeDotsButton.click();

        // Clicks edit language button to open edit dialog.
        const editLanguageButton =
            appLanguagesPage.shadowRoot!.querySelector<HTMLButtonElement>(
                '#editLanguage');
        assertTrue(!!editLanguageButton, 'editLanguageButton not found');
        editLanguageButton.click();
        // Wait until dialog is opened.
        flushTasks();

        // Verify edit dialog is opened and contains the correct app info.
        const appLanguageEditDialog =
            appLanguagesPage.shadowRoot!.querySelector(
                'app-language-selection-dialog');
        assertTrue(
            !!appLanguageEditDialog,
            'app-language-selection-dialog not found.');
        assertEquals(testAppTitle, appLanguageEditDialog.app.title);
      });

  test(
      'Click three-dots button and reset language, selected language is ' +
          'set to device language',
      async () => {
        const testAppId = 'click-three-dots-button-and-reset-language';
        const testLocaleTag = 'testLocaleTag';
        const arcOptions: AppConfig = {
          type: AppType.kArc,
          supportedLocales: [{
            localeTag: testLocaleTag,
            displayName: '',
            nativeDisplayName: '',
          }],
          selectedLocale: {
            localeTag: testLocaleTag,
            displayName: '',
            nativeDisplayName: '',
          },
        };
        await addApp(testAppId, arcOptions);

        // Clicks three-dots button to open dropdown menu.
        const appItems = getAppItems();
        assertEquals(1, appItems.length);
        const threeDotsButton = appItems[0]!.querySelector(crIconButtonTag);
        assertTrue(!!threeDotsButton, 'threeDotsButton not found');
        threeDotsButton.click();

        // Clicks reset language button.
        const resetLanguageButton =
            appLanguagesPage.shadowRoot!.querySelector<HTMLButtonElement>(
                '#resetLanguage');
        assertTrue(!!resetLanguageButton, 'resetLanguageButton not found');
        resetLanguageButton.click();
        // Wait for AppManagementStore to be updated.
        await fakeHandler.flushPipesForTesting();

        // Verify app language is reset back to device language.
        const app = AppManagementStore.getInstance().data.apps[testAppId];
        assertEquals(deviceLanguageLocaleTag, app!.selectedLocale!.localeTag);
      });
});