chromium/chrome/test/data/webui/settings/settings_prefs_test.ts

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

// clang-format off
import 'chrome://settings/settings.js';

import {CrSettingsPrefs} from 'chrome://settings/settings.js';
import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';

import {prefsTestCases} from './settings_prefs_test_cases.js';

// clang-format on
suite('CrSettingsPrefs', function() {
  /**
   * Prefs instance created before each test.
   */
  let prefs: SettingsPrefsElement;

  let fakeApi: FakeSettingsPrivate;

  /**
   * @param {!Object} Pref store from <settings-prefs>.
   * @param {string} Pref key of the pref to return.
   */
  function getPrefFromKey(prefStore: any, key: string):
      chrome.settingsPrivate.PrefObject|undefined {
    const path = key.split('.');
    let pref = prefStore;
    for (const part of path) {
      pref = pref[part];
      if (!pref) {
        return undefined;
      }
    }
    return pref;
  }

  /**
   * Checks that the fake API pref store contains the expected values.
   * @param testCaseValueIndex The index of possible next values
   *     from the test case to check.
   */
  function assertFakeApiPrefsSet(testCaseValueIndex: number) {
    for (const testCase of prefsTestCases) {
      const expectedValue =
          JSON.stringify(testCase.nextValues[testCaseValueIndex]);
      const prefsObject = fakeApi.prefs[testCase.pref.key]!;
      const actualValue = JSON.stringify(prefsObject.value);
      assertEquals(expectedValue, actualValue, testCase.pref.key);
    }
  }

  /**
   * Checks that the <settings-prefs> contains the expected values.
   * @param testCaseValueIndex The index of possible next values
   *     from the test case to check.
   */
  function assertPrefsSet(testCaseValueIndex: number) {
    for (const testCase of prefsTestCases) {
      const expectedValue =
          JSON.stringify(testCase.nextValues[testCaseValueIndex]);
      const actualValue =
          JSON.stringify(prefs.get('prefs.' + testCase.pref.key + '.value'));
      assertEquals(expectedValue, actualValue);
    }
  }

  // Initialize a <settings-prefs> before each test.
  setup(function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;

    // Override chrome.settingsPrivate with FakeSettingsPrivate.
    fakeApi = new FakeSettingsPrivate(prefsTestCases.map(function(testCase) {
      return testCase.pref;
    }));
    CrSettingsPrefs.deferInitialization = true;

    prefs = document.createElement('settings-prefs');
    document.body.appendChild(prefs);
    prefs.initialize(fakeApi);

    // getAllPrefs is asynchronous, so return the prefs promise.
    return CrSettingsPrefs.initialized;
  });

  teardown(function() {
    CrSettingsPrefs.resetForTesting();
    CrSettingsPrefs.deferInitialization = false;
    prefs.resetForTesting();

    document.body.innerHTML = window.trustedTypes!.emptyHTML;
  });

  test('receives and caches prefs', function testGetPrefs() {
    // Test that each pref has been successfully copied to the Polymer
    // |prefs| property.
    for (const key in fakeApi.prefs) {
      const expectedPref = fakeApi.prefs[key];
      const actualPref = getPrefFromKey(prefs.prefs, key);
      assertNotEquals(undefined, actualPref);
      assertEquals(JSON.stringify(expectedPref), JSON.stringify(actualPref));
    }
  });

  test('forwards pref changes to API', async function testSetPrefs() {
    // Test that settings-prefs uses the setPref API.
    for (const testCase of prefsTestCases) {
      prefs.set(
          'prefs.' + testCase.pref.key + '.value',
          structuredClone(testCase.nextValues[0]));
    }
    // Check that setPref has been called for the right values.
    assertFakeApiPrefsSet(0);

    // Test that when setPref fails, the pref is reverted locally.
    for (const testCase of prefsTestCases) {
      fakeApi.failNextSetPref();
      fakeApi.resetResolver('getPref');
      fakeApi.resetResolver('setPref');
      prefs.set(
          'prefs.' + testCase.pref.key + '.value',
          structuredClone(testCase.nextValues[1]));
      if (testCase.nextValues[0] !== testCase.nextValues[1]) {
        const key1 = (await fakeApi.whenCalled('setPref')).key;
        assertEquals(testCase.pref.key, key1);
        const key2 = await fakeApi.whenCalled('getPref');
        assertEquals(testCase.pref.key, key2);
      }
      const expectedValue = JSON.stringify(testCase.nextValues[0]);
      const actualValue =
          JSON.stringify(prefs.get('prefs.' + testCase.pref.key + '.value'));
      assertEquals(expectedValue, actualValue);
    }

    // Test that setPref is not called when the pref doesn't change.
    fakeApi.disallowSetPref();
    for (const testCase of prefsTestCases) {
      prefs.set(
          'prefs.' + testCase.pref.key + '.value',
          structuredClone(testCase.nextValues[0]));
    }
    assertFakeApiPrefsSet(0);
    fakeApi.allowSetPref();
  });

  test('responds to API changes', function testOnChange() {
    // Changes from the API should not result in those changes being sent
    // back to the API, as this could trigger a race condition.
    fakeApi.disallowSetPref();
    let prefChanges = [];
    for (const testCase of prefsTestCases) {
      prefChanges.push({key: testCase.pref.key, value: testCase.nextValues[0]});
    }

    // Send a set of changes.
    fakeApi.sendPrefChanges(prefChanges);
    assertPrefsSet(0);

    prefChanges = [];
    for (const testCase of prefsTestCases) {
      prefChanges.push({key: testCase.pref.key, value: testCase.nextValues[1]});
    }

    // Send a second set of changes.
    fakeApi.sendPrefChanges(prefChanges);
    assertPrefsSet(1);

    // Send the same set of changes again -- nothing should happen.
    fakeApi.sendPrefChanges(prefChanges);
    assertPrefsSet(1);
  });
});