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

// Copyright 2016 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 type {CrTooltipElement, StorageAccessEmbeddingException, StorageAccessSiteException, ChooserException, DefaultContentSetting, OriginInfo, RawChooserException, RawSiteException, SiteException, SiteGroup} from 'chrome://settings/lazy_load.js';
import {ChooserType, ContentSetting, ContentSettingProvider, ContentSettingsTypes, SiteSettingSource} from 'chrome://settings/lazy_load.js';
import type {Route} from 'chrome://settings/settings.js';
import {Router} from 'chrome://settings/settings.js';
import {assertEquals} from 'chrome://webui-test/chai_assert.js';
// clang-format on

/**
 * Helper to create an object containing a ContentSettingsType key to array or
 * object value. This is a convenience function that can eventually be
 * replaced with ES6 computed properties.
 * @param contentType The ContentSettingsType to use as the key.
 * @param value The value to map to |contentType|.
 */
export function createContentSettingTypeToValuePair(
    contentType: ContentSettingsTypes,
    value: any): {setting: ContentSettingsTypes, value: any} {
  return {setting: contentType, value: value};
}

/**
 * Helper to create a mock DefaultContentSetting.
 * @param override An object with a subset of the properties of
 *     DefaultContentSetting. Properties defined in |override| will
 *     overwrite the defaults in this function's return value.
 */
export function createDefaultContentSetting(
    override?: Partial<DefaultContentSetting>): DefaultContentSetting {
  return Object.assign(
      {
        setting: ContentSetting.ASK,
        source: ContentSettingProvider.PREFERENCE,
      },
      override || {});
}

/**
 * Helper to create a mock RawSiteException.
 * @param origin The origin to use for this RawSiteException.
 * @param override An object with a subset of the properties of
 *     RawSiteException. Properties defined in |override| will overwrite the
 *     defaults in this function's return value.
 */
export function createRawSiteException(
    origin: string, override?: Partial<RawSiteException>): RawSiteException {
  return Object.assign(
      {
        embeddingOrigin: origin,
        incognito: false,
        origin: origin,
        displayName: '',
        setting: ContentSetting.ALLOW,
        source: SiteSettingSource.PREFERENCE,
        isEmbargoed: false,
        type: '',
      },
      override || {});
}

/**
 * Helper to create a mock RawChooserException.
 * @param chooserType The chooser exception type.
 * @param sites A list of SiteExceptions corresponding to the chooser exception.
 * @param override An object with a subset of the properties of
 *     RawChooserException. Properties defined in |override| will overwrite
 *     the defaults in this function's return value.
 */
export function createRawChooserException(
    chooserType: ChooserType, sites: RawSiteException[],
    override?: Partial<RawChooserException>): RawChooserException {
  return Object.assign(
      {
        chooserType: chooserType,
        displayName: '',
        object: {},
        sites: sites,
      },
      override || {});
}

/**
 * In the real (non-test) code, this data comes from the C++ handler.
 * Only used for tests.
 */
export interface SiteSettingsPref {
  defaults: {[key in ContentSettingsTypes]: DefaultContentSetting};
  exceptions: {[key in ContentSettingsTypes]: RawSiteException[]};
  chooserExceptions: {[key in ContentSettingsTypes]: RawChooserException[]};
}

/**
 * Helper to create a mock SiteSettingsPref.
 * @param defaultsList A list of DefaultContentSettings and the content settings
 *     they apply to, which will overwrite the defaults in the SiteSettingsPref
 *     returned by this function.
 * @param exceptionsList A list of RawSiteExceptions and the content settings
 *     they apply to, which will overwrite the exceptions in the
 *     SiteSettingsPref returned by this function.
 * @param chooserExceptionsList A list of RawChooserExceptions and the chooser
 *     type that they apply to, which will overwrite the exceptions in the
 *     SiteSettingsPref returned by this function.
 */
export function createSiteSettingsPrefs(
    defaultsList:
        Array<{setting: ContentSettingsTypes, value: DefaultContentSetting}>,
    exceptionsList:
        Array<{setting: ContentSettingsTypes, value: RawSiteException[]}>,
    chooserExceptionsList:
        Array<{setting: ContentSettingsTypes, value: RawChooserException[]}> =
            []): SiteSettingsPref {
  // These test defaults reflect the actual defaults assigned to each
  // ContentSettingType, but keeping these in sync shouldn't matter for tests.
  const defaults: {[key in ContentSettingsTypes]: DefaultContentSetting} = {} as
      any;
  for (const type in ContentSettingsTypes) {
    defaults[ContentSettingsTypes[type as keyof typeof ContentSettingsTypes]] =
        createDefaultContentSetting({});
  }
  defaults[ContentSettingsTypes.ANTI_ABUSE].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.COOKIES].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.IMAGES].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.JAVASCRIPT].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.JAVASCRIPT_JIT].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.SOUND].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.POPUPS].setting = ContentSetting.BLOCK;
  defaults[ContentSettingsTypes.PROTOCOL_HANDLERS].setting =
      ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.BACKGROUND_SYNC].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.ADS].setting = ContentSetting.BLOCK;
  defaults[ContentSettingsTypes.SENSORS].setting = ContentSetting.ALLOW;
  defaults[ContentSettingsTypes.USB_DEVICES].setting = ContentSetting.ASK;
  defaultsList.forEach((override) => {
    defaults[override.setting] = override.value;
  });

  const chooserExceptions:
      {[key in ContentSettingsTypes]: RawChooserException[]} = {} as any;
  const exceptions: {[key in ContentSettingsTypes]: RawSiteException[]} = {} as
      any;
  for (const t in ContentSettingsTypes) {
    const type = t as keyof typeof ContentSettingsTypes;
    chooserExceptions[ContentSettingsTypes[type]] = [];
    exceptions[ContentSettingsTypes[type]] = [];
  }
  exceptionsList.forEach(override => {
    exceptions[override.setting] = override.value;
  });
  chooserExceptionsList.forEach(override => {
    chooserExceptions[override.setting] = override.value;
  });

  return {
    chooserExceptions: chooserExceptions,
    defaults: defaults,
    exceptions: exceptions,
  };
}

/**
 * Creates a groupingKey that maps to the given id. This will not
 * have the same internal format as the groupingKey returned by
 * SiteSettingsHelper.
 * @param id An id that can uniquely represent a SiteGroup.
 */
export function groupingKey(id: string): string {
  // Base64 encode the id to make sure that if an eTLD+1 or origin is used
  // somewhere instead of a groupingKey, it won't accidentally have the
  // correct groupingKey value.
  return btoa(id);
}

/**
 * Helper to create a mock SiteGroup.
 * @param owningEntity The eTLD+1 or origin that owns this group.
 * @param displayName The user-visible group name.
 * @param originList A list of the origins with the same eTLD+1.
 * @param mockUsage The override initial usage value for each origin in the site
 *     group.
 */
export function createSiteGroup(
    owningEntity: string|URL, displayName: string, originList: string[],
    mockUsage?: number): SiteGroup {
  if (mockUsage === undefined) {
    mockUsage = 0;
  }
  const originInfoList =
      originList.map((origin) => createOriginInfo(origin, {usage: mockUsage}));
  return {
    groupingKey: groupingKey(owningEntity.toString()),
    etldPlus1: typeof owningEntity === 'string' ? owningEntity : undefined,
    displayName,
    origins: originInfoList,
    numCookies: 0,
    hasInstalledPWA: false,
  };
}

export function createOriginInfo(
    origin: string, override?: Partial<OriginInfo>): OriginInfo {
  return Object.assign(
      {
        origin: origin,
        engagement: 0,
        usage: 0,
        numCookies: 0,
        hasPermissionSettings: false,
        isInstalled: false,
        isPartitioned: false,
      },
      override || {});
}

/**
 * Helper to retrieve the category of a permission from the given
 * |chooserType|.
 * @param chooserType The chooser type of the permission.
 */
export function getContentSettingsTypeFromChooserType(chooserType: ChooserType):
    ContentSettingsTypes|null {
  switch (chooserType) {
    case ChooserType.HID_DEVICES:
      return ContentSettingsTypes.HID_DEVICES;
    case ChooserType.SERIAL_PORTS:
      return ContentSettingsTypes.SERIAL_PORTS;
    case ChooserType.USB_DEVICES:
      return ContentSettingsTypes.USB_DEVICES;
    case ChooserType.BLUETOOTH_DEVICES:
      return ContentSettingsTypes.BLUETOOTH_DEVICES;
    default:
      return null;
  }
}

export function setupPopstateListener() {
  window.addEventListener('popstate', function() {
    // On pop state, do not push the state onto the window.history again.
    const routerInstance = Router.getInstance();
    routerInstance.setCurrentRoute(
        routerInstance.getRouteForPath(window.location.pathname) ||
            (routerInstance.getRoutes() as {BASIC: Route}).BASIC,
        new URLSearchParams(window.location.search), true);
  });
}

/**
 * Helper to assert that a cr-tooltip element is visually
 * hidden but still accessible by screen readers.
 */
export function assertTooltipIsHidden(tooltip: CrTooltipElement) {
  const tooltipStyle = window.getComputedStyle(tooltip);
  assertEquals('rect(0px, 0px, 0px, 0px)', tooltipStyle.clip);
  assertEquals('1px', tooltipStyle.height);
  assertEquals('1px', tooltipStyle.width);
  assertEquals('hidden', tooltipStyle.overflow);
}

/**
 * Helper to create a mock SiteException.
 * @param origin The origin to use for this SiteException.
 * @param override An object with a subset of the properties of
 *     SiteException. Properties defined in |override| will overwrite the
 *     defaults in this function's return value.
 */
export function createSiteException(
    origin: string, override?: Partial<SiteException>): SiteException {
  return Object.assign(
      {
        category: ContentSettingsTypes.USB_DEVICES,
        embeddingOrigin: origin,
        incognito: false,
        origin: origin,
        displayName: origin,
        setting: ContentSetting.DEFAULT,
        settingDetail: null,
        enforcement: null,
        controlledBy: chrome.settingsPrivate.ControlledBy.PRIMARY_USER,
        isEmbargoed: false,
      },
      override || {});
}

/**
 * Helper to create a mock of a group of Storage Access Site Exceptions.
 * @param origin The origin to use for this group.
 * @param override An object with a subset of the properties of
 *     StorageAccessSiteException. Properties defined in |override| will
 * overwrite the defaults in this function's return value.
 */
export function createStorageAccessSiteException(
    origin: string,
    override?: Partial<StorageAccessSiteException>,
    embeddingOrigin?: string,
    ): StorageAccessSiteException {
  return Object.assign(
      {
        origin: origin,
        displayName: origin,
        setting: ContentSetting.ALLOW,
        openDescription: '',
        closeDescription: '',
        exceptions: [createStorageAccessEmbeddingException(
            embeddingOrigin || 'google.com')],
      },
      override || {});
}

/**
 * Helper to create a mock of an Storage Access embedding site exception.
 * @param embeddingOrigin The embedding origin to use for this SiteException.
 * @param override An object with a subset of the properties of
 *     StorageAccessEmbeddingException. Properties defined in |override| will
 * overwrite the defaults in this function's return value.
 */
export function createStorageAccessEmbeddingException(
    embeddingOrigin: string,
    override?: Partial<StorageAccessEmbeddingException>):
    StorageAccessEmbeddingException {
  return Object.assign(
      {
        embeddingOrigin: embeddingOrigin,
        embeddingDisplayName: embeddingOrigin,
        description: '',
        incognito: false,
      },
      override || {});
}

/**
 * Helper to create a mock ChooserException.
 * @param chooserType The chooser exception type.
 * @param sites A list of SiteExceptions corresponding to the chooser exception.
 * @param override An object with a subset of the properties of
 *     ChooserException. Properties defined in |override| will overwrite
 *     the defaults in this function's return value.
 */
export function createChooserException(
    chooserType: ChooserType, sites: SiteException[],
    override?: Partial<ChooserException>): ChooserException {
  return Object.assign(
      {
        chooserType: chooserType,
        displayName: '',
        object: {},
        sites: sites,
      },
      override || {});
}