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

// Copyright 2017 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 {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import type {AllSitesElement, SiteGroup} from 'chrome://settings/lazy_load.js';
import {ContentSetting, ContentSettingsTypes, SiteSettingsPrefsBrowserProxyImpl, SortMethod} from 'chrome://settings/lazy_load.js';
import {CrSettingsPrefs, DeleteBrowsingDataAction, MetricsBrowserProxyImpl, Router, routes} from 'chrome://settings/settings.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';

import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
import type {SiteSettingsPref} from './test_util.js';
import {createContentSettingTypeToValuePair, createOriginInfo, createRawSiteException, createSiteGroup, createSiteSettingsPrefs, groupingKey} from './test_util.js';
import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';


// clang-format on

suite('DisableRelatedWebsiteSets', function() {
  /**
   * An example eTLD+1 Object with multiple origins grouped under it.
   */
  const TEST_MULTIPLE_SITE_GROUP =
      createSiteGroup('example.com', 'example.com', [
        'http://subdomain.example.com/',
        'https://www.example.com/',
        'https://login.example.com/',
      ]);

  /**
   * An example eTLD+1 Object with a single origin grouped under it.
   */
  const TEST_SINGLE_SITE_GROUP = createSiteGroup('example.com', 'example.com', [
    'https://single.example.com/',
  ]);

  /**
   * An example pref with multiple categories and multiple allow/block
   * state.
   */
  let prefsVarious: SiteSettingsPref;

  let testElement: AllSitesElement;
  let metricsBrowserProxy: TestMetricsBrowserProxy;


  /**
   * The mock proxy object to use during test.
   */
  let browserProxy: TestSiteSettingsPrefsBrowserProxy;

  suiteSetup(function() {
    CrSettingsPrefs.setInitialized();

    loadTimeData.overrideValues({
      firstPartySetsUIEnabled: false,
    });
  });

  suiteTeardown(function() {
    CrSettingsPrefs.resetForTesting();
  });

  // Initialize a site-list before each test.
  setup(async function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;

    prefsVarious = createSiteSettingsPrefs([], [
      createContentSettingTypeToValuePair(
          ContentSettingsTypes.GEOLOCATION,
          [
            createRawSiteException('https://foo.com'),
            createRawSiteException('https://bar.com', {
              setting: ContentSetting.BLOCK,
            }),
          ]),
      createContentSettingTypeToValuePair(
          ContentSettingsTypes.NOTIFICATIONS,
          [
            createRawSiteException('https://google.com', {
              setting: ContentSetting.BLOCK,
            }),
            createRawSiteException('https://bar.com', {
              setting: ContentSetting.BLOCK,
            }),
            createRawSiteException('https://foo.com', {
              setting: ContentSetting.BLOCK,
            }),
          ]),
    ]);
    browserProxy = new TestSiteSettingsPrefsBrowserProxy();
    SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
    metricsBrowserProxy = new TestMetricsBrowserProxy();
    MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy);
    testElement = document.createElement('all-sites');
    assertTrue(!!testElement);
    document.body.appendChild(testElement);
  });

  teardown(function() {
    // The code being tested changes the Route. Reset so that state is not
    // leaked across tests.
    Router.getInstance().resetRouteForTesting();
  });

  /**
   * Configures the test element.
   * @param prefs The prefs to use.
   * @param sortOrder the URL param used to establish default sort order.
   */
  function setUpAllSites(prefs: SiteSettingsPref, sortOrder?: SortMethod) {
    browserProxy.setPrefs(prefs);
    if (sortOrder) {
      Router.getInstance().navigateTo(
          routes.SITE_SETTINGS_ALL, new URLSearchParams(`sort=${sortOrder}`));
    } else {
      Router.getInstance().navigateTo(routes.SITE_SETTINGS_ALL);
    }
  }

  function getSubstitutedString(messageId: string, substitute: string): string {
    return loadTimeData.substituteString(
        testElement.i18n(messageId), substitute);
  }

  test('All sites list populated', async function() {
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    assertEquals(3, testElement.siteGroupMap.size);

    // Flush to be sure list container is populated.
    flush();
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(3, siteEntries.length);
  });

  test('search query filters list', async function() {
    const SEARCH_QUERY = 'foo';
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    // Flush to be sure list container is populated.
    flush();
    let siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(3, siteEntries.length);

    browserProxy.resetResolver('getAllSites');
    const searchParams = new URLSearchParams(
        'searchSubpage=' + encodeURIComponent(SEARCH_QUERY));
    const currentRoute = Router.getInstance().getCurrentRoute();
    Router.getInstance().navigateTo(currentRoute, searchParams);
    testElement.filter = SEARCH_QUERY;

    flush();
    // Changing filter shouldn't trigger additional getAllSites calls.
    assertEquals(0, browserProxy.getCallCount('getAllSites'));
    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    const hiddenSiteEntries = Array.from(
        testElement.shadowRoot!.querySelectorAll('site-entry[hidden]'));
    assertEquals(1, siteEntries.length - hiddenSiteEntries.length);

    for (const entry of siteEntries) {
      if (!hiddenSiteEntries.includes(entry)) {
        assertTrue(entry.siteGroup.origins.some(origin => {
          return origin.origin.includes(SEARCH_QUERY);
        }));
      }
    }
  });

  test('can be sorted by most visited', async function() {
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);

    await browserProxy.whenCalled('getAllSites');

    // Add additional origins and artificially insert fake engagement scores
    // to sort.
    assertEquals(3, testElement.siteGroupMap.size);
    const fooSiteGroup = testElement.siteGroupMap.get(groupingKey('foo.com'))!;
    fooSiteGroup.origins.push(
        createOriginInfo('https://login.foo.com', {engagement: 20}));
    assertEquals(2, fooSiteGroup.origins.length);
    fooSiteGroup.origins[0]!.engagement = 50.4;
    const googleSiteGroup =
        testElement.siteGroupMap.get(groupingKey('google.com'))!;
    assertEquals(1, googleSiteGroup.origins.length);
    googleSiteGroup.origins[0]!.engagement = 55.1261;
    const barSiteGroup = testElement.siteGroupMap.get(groupingKey('bar.com'))!;
    assertEquals(1, barSiteGroup.origins.length);
    barSiteGroup.origins[0]!.engagement = 0.5235;

    // 'Most visited' is the default sort method, so sort by a different
    // method first to ensure changing to 'Most visited' works.
    testElement.$.sortMethod.value = 'name';
    testElement.$.sortMethod.dispatchEvent(new CustomEvent('change'));
    flush();
    let siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals('bar.com', siteEntries[0]!.$.displayName.innerText.trim());
    assertEquals('foo.com', siteEntries[1]!.$.displayName.innerText.trim());
    assertEquals('google.com', siteEntries[2]!.$.displayName.innerText.trim());

    testElement.$.sortMethod.value = 'most-visited';
    testElement.$.sortMethod.dispatchEvent(new CustomEvent('change'));
    flush();
    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    // Each site entry is sorted by its maximum engagement, so expect
    // 'foo.com' to come after 'google.com'.
    assertEquals('google.com', siteEntries[0]!.$.displayName.innerText.trim());
    assertEquals('foo.com', siteEntries[1]!.$.displayName.innerText.trim());
    assertEquals('bar.com', siteEntries[2]!.$.displayName.innerText.trim());
  });

  test('dynamic filtered clear data button strings', async function() {
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');

    const clearAllButton = testElement.$.clearAllButton;
    assertEquals(
        loadTimeData.getString('siteSettingsDeleteAllStorageLabel'),
        clearAllButton.innerText.trim());

    // Setting a filter, text should change.
    testElement.filter = 'foo';
    await flushTasks();
    assertEquals(
        loadTimeData.getString('siteSettingsDeleteDisplayedStorageLabel'),
        clearAllButton.innerText.trim());

    // Removing the filter.
    testElement.filter = '';
    await flushTasks();
    assertEquals(
        loadTimeData.getString('siteSettingsDeleteAllStorageLabel'),
        clearAllButton.innerText.trim());
  });

  test('dynamic filtered total usage strings', async function() {
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    // Removing extra unwanted site entries.
    testElement.siteGroupMap.delete(groupingKey('google.com'));
    testElement.forceListUpdateForTesting();
    await flushTasks();

    const clearLabel = testElement.$.clearLabel;
    assertEquals(
        getSubstitutedString('siteSettingsClearAllStorageDescription', '100 B'),
        clearLabel.innerText.trim());

    // Setting a filter, 'foo.com' exists but doesn't have storage.
    testElement.filter = 'foo';
    await flushTasks();
    assertEquals(
        getSubstitutedString(
            'siteSettingsClearDisplayedStorageDescription', '0 B'),
        clearLabel.innerText.trim());

    // Changing the filter, 100 B given to 'bar.com; in the test proxy.
    testElement.filter = 'bar';
    await flushTasks();
    assertEquals(
        getSubstitutedString(
            'siteSettingsClearDisplayedStorageDescription', '100 B'),
        clearLabel.innerText.trim());

    // Remove the filter, without changing the total amount of storage.
    testElement.filter = '';
    await flushTasks();
    assertEquals(
        getSubstitutedString('siteSettingsClearAllStorageDescription', '100 B'),
        clearLabel.innerText.trim());
  });

  test('dynamic filtered clear data confirmation strings', async function() {
    interface DialogState {
      filter: boolean;
      appInstalled: boolean;
      storage: boolean;
      title: string;
      description: string;
      signout: string;
    }

    const dialogStates: DialogState[] = [
      {
        filter: false,
        appInstalled: false,
        storage: true,
        title: 'siteSettingsDeleteAllStorageDialogTitle',
        description: 'siteSettingsDeleteAllStorageConfirmation',
        signout: 'siteSettingsClearAllStorageSignOut',
      },
      {
        filter: false,
        appInstalled: true,
        storage: true,
        title: 'siteSettingsDeleteAllStorageDialogTitle',
        description: 'siteSettingsDeleteAllStorageConfirmationInstalled',
        signout: 'siteSettingsClearAllStorageSignOut',
      },
      {
        filter: true,
        appInstalled: false,
        storage: true,
        title: 'siteSettingsDeleteDisplayedStorageDialogTitle',
        description: 'siteSettingsDeleteDisplayedStorageConfirmation',
        signout: 'siteSettingsClearDisplayedStorageSignOut',
      },
      {
        filter: true,
        appInstalled: false,
        storage: false,
        title: 'siteSettingsDeleteDisplayedStorageDialogTitle',
        description: 'siteSettingsDeleteDisplayedStorageConfirmation',
        signout: 'siteSettingsClearDisplayedStorageSignOut',
      },
      {
        filter: true,
        appInstalled: true,
        storage: false,
        title: 'siteSettingsDeleteDisplayedStorageDialogTitle',
        description: 'siteSettingsDeleteDisplayedStorageConfirmationInstalled',
        signout: 'siteSettingsClearDisplayedStorageSignOut',
      },
      {
        filter: true,
        appInstalled: true,
        storage: true,
        title: 'siteSettingsDeleteDisplayedStorageDialogTitle',
        description: 'siteSettingsDeleteDisplayedStorageConfirmationInstalled',
        signout: 'siteSettingsClearDisplayedStorageSignOut',
      },
    ];

    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    // Removing extra unwanted site entries.
    testElement.siteGroupMap.delete(groupingKey('google.com'));

    const clearAllButton =
        testElement.$.clearAllButton.querySelector('cr-button')!;
    const confirmClearAllData = testElement.$.confirmClearAllData.get();

    // Open the clear all data dialog.
    assertFalse(confirmClearAllData.open, 'closed dialog');
    clearAllButton.click();
    assertTrue(confirmClearAllData.open, 'open dialog');

    for (const state of dialogStates) {
      assertTrue(state.filter || state.storage, 'valid state');

      if (state.storage) {  // 'bar' has storage of 100 B
        testElement.filter = state.filter ? 'bar' : '';
      } else {  // 'foo' has storage of 0 B
        testElement.filter = state.filter ? 'foo' : '';
      }

      testElement.siteGroupMap.get(groupingKey('foo.com'))!.hasInstalledPWA =
          state.appInstalled;
      testElement.siteGroupMap.get(groupingKey('bar.com'))!.hasInstalledPWA =
          state.appInstalled;

      testElement.forceListUpdateForTesting();
      await flushTasks();

      const confirmationTitle =
          confirmClearAllData.querySelector<HTMLElement>(
                                 '[slot=title]')!.innerText.trim();
      const confirmationDescription =
          confirmClearAllData
              .querySelector<HTMLElement>(
                  '#clearAllStorageDialogDescription')!.innerText.trim();
      const confirmationSignOutLabel =
          confirmClearAllData
              .querySelector<HTMLElement>(
                  '#clearAllStorageDialogSignOutLabel')!.innerText.trim();

      assertEquals(loadTimeData.getString(state.title), confirmationTitle);
      assertEquals(
          getSubstitutedString(
              state.description, state.storage ? '100 B' : '0 B'),
          confirmationDescription);
      assertEquals(
          loadTimeData.getString(state.signout), confirmationSignOutLabel);
    }
  });

  test('clear data "no sites" string', async function() {
    testElement.siteGroupMap.set(
        TEST_MULTIPLE_SITE_GROUP.groupingKey,
        structuredClone(TEST_MULTIPLE_SITE_GROUP));
    const googleSiteGroup = createSiteGroup('google.com', 'google.com', [
      'https://www.google.com',
      'https://docs.google.com',
      'https://mail.google.com',
    ]);
    testElement.siteGroupMap.set(googleSiteGroup.groupingKey, googleSiteGroup);
    testElement.filter = 'google';
    testElement.forceListUpdateForTesting();
    await flushTasks();

    assertFalse(isChildVisible(testElement, '#noSitesFoundText'));

    clearDataViaClearAllButton('action-button');
    await flushTasks();

    assertTrue(isChildVisible(testElement, '#noSitesFoundText'));
  });

  test('can be sorted by storage', async function() {
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    flush();
    let siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    // Add additional origins to SiteGroups with cookies to simulate their
    // being grouped entries, plus add local storage.
    siteEntries[0]!.siteGroup.origins[0]!.usage = 900;
    siteEntries[1]!.siteGroup.origins.push(createOriginInfo('http://bar.com'));
    siteEntries[1]!.siteGroup.origins[0]!.usage = 500;
    siteEntries[1]!.siteGroup.origins[1]!.usage = 500;
    siteEntries[2]!.siteGroup.origins.push(
        createOriginInfo('http://google.com'));

    testElement.$.sortMethod.dispatchEvent(new CustomEvent('change'));
    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    // Verify all sites is not sorted by storage.
    assertEquals(3, siteEntries.length);
    assertEquals('foo.com', siteEntries[0]!.$.displayName.innerText.trim());
    assertEquals('bar.com', siteEntries[1]!.$.displayName.innerText.trim());
    assertEquals('google.com', siteEntries[2]!.$.displayName.innerText.trim());

    // Change the sort method, then verify all sites is now sorted by
    // name.
    testElement.$.sortMethod.value = 'data-stored';
    testElement.$.sortMethod.dispatchEvent(new CustomEvent('change'));

    flush();
    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(
        'bar.com',
        siteEntries[0]!.shadowRoot!
            .querySelector<HTMLElement>(
                '#displayName .url-directionality')!.innerText.trim());
    assertEquals(
        'foo.com',
        siteEntries[1]!.shadowRoot!
            .querySelector<HTMLElement>(
                '#displayName .url-directionality')!.innerText.trim());
    assertEquals(
        'google.com',
        siteEntries[2]!.shadowRoot!
            .querySelector<HTMLElement>(
                '#displayName .url-directionality')!.innerText.trim());
  });

  test('can be sorted by storage by passing URL param', async function() {
    // The default sorting (most visited) will have the ascending storage
    // values. With the URL param, we expect the sites to be sorted by usage in
    // descending order.
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    setUpAllSites(prefsVarious, SortMethod.STORAGE);
    testElement = document.createElement('all-sites');
    document.body.appendChild(testElement);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    flush();
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');

    assertEquals(
        'google.com',
        siteEntries[0]!.shadowRoot!
            .querySelector<HTMLElement>(
                '#displayName .url-directionality')!.innerText.trim());
    assertEquals(
        'bar.com',
        siteEntries[1]!.shadowRoot!
            .querySelector<HTMLElement>(
                '#displayName .url-directionality')!.innerText.trim());
    assertEquals(
        'foo.com',
        siteEntries[2]!.shadowRoot!
            .querySelector<HTMLElement>(
                '#displayName .url-directionality')!.innerText.trim());
  });

  test('can be sorted by name', async function() {
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    flush();
    let siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');

    // Verify all sites is not sorted by name.
    assertEquals(3, siteEntries.length);
    assertEquals('foo.com', siteEntries[0]!.$.displayName.innerText.trim());
    assertEquals('bar.com', siteEntries[1]!.$.displayName.innerText.trim());
    assertEquals('google.com', siteEntries[2]!.$.displayName.innerText.trim());

    // Change the sort method, then verify all sites is now sorted by name.
    testElement.$.sortMethod.value = 'name';
    testElement.$.sortMethod.dispatchEvent(new CustomEvent('change'));
    flush();
    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals('bar.com', siteEntries[0]!.$.displayName.innerText.trim());
    assertEquals('foo.com', siteEntries[1]!.$.displayName.innerText.trim());
    assertEquals('google.com', siteEntries[2]!.$.displayName.innerText.trim());
  });

  test('can sort by name by passing URL param', async function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    setUpAllSites(prefsVarious, SortMethod.NAME);
    testElement = document.createElement('all-sites');
    document.body.appendChild(testElement);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    flush();
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');

    assertEquals('bar.com', siteEntries[0]!.$.displayName.innerText.trim());
    assertEquals('foo.com', siteEntries[1]!.$.displayName.innerText.trim());
    assertEquals('google.com', siteEntries[2]!.$.displayName.innerText.trim());
  });

  test('merging additional SiteGroup lists works', async function() {
    setUpAllSites(prefsVarious);
    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
    await browserProxy.whenCalled('getAllSites');
    flush();
    let siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(3, siteEntries.length);

    // Pretend an additional set of SiteGroups were added.
    const fooEtldPlus1 = 'foo.com';
    const addEtldPlus1 = 'additional-site.net';
    const fooOrigin = 'https://login.foo.com';
    const addOrigin = 'http://www.additional-site.net';
    const STORAGE_SITE_GROUP_LIST: SiteGroup[] = [
      {
        // Test merging an existing site works, with overlapping origin lists.
        groupingKey: groupingKey(fooEtldPlus1),
        displayName: fooEtldPlus1,
        origins: [
          createOriginInfo(fooOrigin),
          createOriginInfo('https://foo.com'),
        ],
        hasInstalledPWA: false,
        numCookies: 0,
      },
      {
        // Test adding a new site entry works.
        groupingKey: groupingKey(addEtldPlus1),
        displayName: addEtldPlus1,
        origins: [createOriginInfo(addOrigin)],
        hasInstalledPWA: false,
        numCookies: 0,
      },
    ];
    testElement.onStorageListFetched(STORAGE_SITE_GROUP_LIST);

    flush();
    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(4, siteEntries.length);

    assertEquals(fooEtldPlus1, siteEntries[0]!.siteGroup.displayName);
    assertEquals(2, siteEntries[0]!.siteGroup.origins.length);
    assertEquals(fooOrigin, siteEntries[0]!.siteGroup.origins[0]!.origin);
    assertEquals(
        'https://foo.com', siteEntries[0]!.siteGroup.origins[1]!.origin);

    assertEquals(addEtldPlus1, siteEntries[3]!.siteGroup.displayName);
    assertEquals(1, siteEntries[3]!.siteGroup.origins.length);
    assertEquals(addOrigin, siteEntries[3]!.siteGroup.origins[0]!.origin);
  });

  function clearDataViaClearAllButton(buttonType: string) {
    assertTrue(
        buttonType === 'cancel-button' || buttonType === 'action-button');
    flush();
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertTrue(siteEntries.length >= 1);
    const clearAllButton =
        testElement.$.clearAllButton.querySelector('cr-button')!;
    const confirmClearAllData = testElement.$.confirmClearAllData.get();

    // Open the clear all data dialog.
    // Test clicking on the clear all button opens the clear all dialog.
    assertFalse(confirmClearAllData.open);
    clearAllButton.click();
    assertTrue(confirmClearAllData.open);

    // Open the clear data dialog and tap the |buttonType| button.
    const actionButtonList =
        testElement.$.confirmClearAllData.get().querySelectorAll<HTMLElement>(
            `.${buttonType}`);
    assertEquals(1, actionButtonList.length);
    actionButtonList[0]!.click();

    // Check the dialog and overflow menu are now both closed.
    assertFalse(confirmClearAllData.open);
  }

  test('cancelling the confirm dialog on clear all data works', function() {
    testElement.siteGroupMap.set(
        TEST_MULTIPLE_SITE_GROUP.groupingKey,
        structuredClone(TEST_MULTIPLE_SITE_GROUP));
    testElement.forceListUpdateForTesting();
    clearDataViaClearAllButton('cancel-button');
  });

  test('clearing data via clear all dialog', async function() {
    // Test when all origins has no permission settings and no data.
    // Clone this object to avoid propagating changes made in this test.
    testElement.siteGroupMap.set(
        TEST_MULTIPLE_SITE_GROUP.groupingKey,
        structuredClone(TEST_MULTIPLE_SITE_GROUP));
    const googleSiteGroup = createSiteGroup('google.com', 'google.com', [
      'https://www.google.com',
      'https://docs.google.com',
      'https://mail.google.com',
    ]);
    testElement.siteGroupMap.set(googleSiteGroup.groupingKey, googleSiteGroup);
    testElement.forceListUpdateForTesting();
    clearDataViaClearAllButton('action-button');
    // Ensure a call was made to clearSiteGroupDataAndCookies.
    assertEquals(2, browserProxy.getCallCount('clearSiteGroupDataAndCookies'));
    assertEquals(testElement.$.allSitesList.items!.length, 0);

    assertEquals(
        DeleteBrowsingDataAction.SITES_SETTINGS_PAGE,
        await metricsBrowserProxy.whenCalled('recordDeleteBrowsingDataAction'));
  });

  test(
      'clear data via clear all button (one origin has permission)',
      async function() {
        // Test when there is one origin has permissions settings.
        // Clone this object to avoid propagating changes made in this test.
        const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
        siteGroup.origins[0]!.hasPermissionSettings = true;
        testElement.siteGroupMap.set(
            siteGroup.groupingKey, structuredClone(siteGroup));
        const googleSiteGroup = createSiteGroup('google.com', 'google.com', [
          'https://www.google.com',
          'https://docs.google.com',
          'https://mail.google.com',
        ]);
        testElement.siteGroupMap.set(
            googleSiteGroup.groupingKey, googleSiteGroup);
        testElement.forceListUpdateForTesting();
        assertEquals(testElement.$.allSitesList.items!.length, 2);
        assertEquals(
            testElement.$.allSitesList.items![0].origins.length,
            siteGroup.origins.length);
        clearDataViaClearAllButton('action-button');
        assertEquals(testElement.$.allSitesList.items!.length, 1);
        assertEquals(testElement.$.allSitesList.items![0].origins.length, 1);

        assertEquals(
            DeleteBrowsingDataAction.SITES_SETTINGS_PAGE,
            await metricsBrowserProxy.whenCalled(
                'recordDeleteBrowsingDataAction'));
      });

  test('clear all button is hidden when the list is empty', async function() {
    // Start with an empty list should hide clear all button.
    assertEquals(testElement.$.allSitesList.items!.length, 0);
    const clearAllButton = testElement.$.clearAllButton;
    assertFalse(isVisible(clearAllButton));

    // Add an entry to site group map.
    const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    await flushTasks();

    // Ensure list is populated and clear all button is shown.
    assertEquals(testElement.$.allSitesList.items!.length, 1);
    assertTrue(isVisible(clearAllButton));

    // Clearing all data should re-hide the button.
    clearDataViaClearAllButton('action-button');
    assertEquals(testElement.$.allSitesList.items!.length, 0);
    assertFalse(isVisible(clearAllButton));
  });

  function removeFirstOrigin() {
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(1, siteEntries.length);
    const originList = siteEntries[0]!.$.originList.get();
    flush();
    const originEntries = originList.querySelectorAll('.hr');
    assertEquals(3, originEntries.length);
    originEntries[0]!.querySelector<HTMLElement>(
                         '#removeOriginButton')!.click();
  }

  function removeFirstSiteGroup() {
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(1, siteEntries.length);
    siteEntries[0]!.$$<HTMLElement>('#removeSiteButton')!.click();
  }

  function confirmDialog() {
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    testElement.$.confirmRemoveSite.get()
        .querySelector<HTMLElement>('.action-button')!.click();
  }

  function cancelDialog() {
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    testElement.$.confirmRemoveSite.get()
        .querySelector<HTMLElement>('.cancel-button')!.click();
  }

  function getString(messageId: string): string {
    return testElement.i18n(messageId);
  }

  test('remove site group', function() {
    testElement.siteGroupMap.set(
        TEST_MULTIPLE_SITE_GROUP.groupingKey,
        structuredClone(TEST_MULTIPLE_SITE_GROUP));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstSiteGroup();
    confirmDialog();

    assertEquals(
        TEST_MULTIPLE_SITE_GROUP.origins.length,
        browserProxy.getCallCount('setOriginPermissions'));
    assertEquals(0, testElement.$.allSitesList.items!.length);
    assertEquals(1, browserProxy.getCallCount('clearSiteGroupDataAndCookies'));
  });

  test('remove origin', async function() {
    const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
    siteGroup.origins[0]!.numCookies = 1;
    siteGroup.origins[1]!.numCookies = 2;
    siteGroup.origins[2]!.numCookies = 3;
    siteGroup.numCookies = 6;
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstOrigin();
    confirmDialog();

    assertEquals(
        siteGroup.origins[0]!.origin,
        await browserProxy.whenCalled(
            'clearUnpartitionedOriginDataAndCookies'));

    const [origin, types, setting] =
        await browserProxy.whenCalled('setOriginPermissions');
    assertEquals(origin, siteGroup.origins[0]!.origin);
    assertEquals(types, null);  // Null affects all content types.
    assertEquals(setting, ContentSetting.DEFAULT);

    assertEquals(
        1, browserProxy.getCallCount('clearUnpartitionedOriginDataAndCookies'));
    assertEquals(1, browserProxy.getCallCount('setOriginPermissions'));
    assertEquals(5, testElement.$.allSitesList.items![0].numCookies);
  });

  test('remove partitioned origin', async function() {
    const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
    siteGroup.origins[0]!.isPartitioned = true;
    siteGroup.origins[0]!.numCookies = 1;
    siteGroup.origins[1]!.numCookies = 2;
    siteGroup.origins[2]!.numCookies = 3;
    siteGroup.numCookies = 6;

    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    // Remove the the partitioned entry, which will have been ordered to the
    // bottom of the displayed origins.
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    const originList = siteEntries[0]!.$.originList.get();
    flush();
    const originEntries = originList.querySelectorAll('.hr');
    assertEquals(3, originEntries.length);
    originEntries[2]!.querySelector<HTMLElement>(
                         '#removeOriginButton')!.click();
    confirmDialog();

    const [origin, groupingKey] =
        await browserProxy.whenCalled('clearPartitionedOriginDataAndCookies');

    assertEquals(siteGroup.origins[0]!.origin, origin);
    assertEquals(siteGroup.groupingKey, groupingKey);
    assertEquals(
        1, browserProxy.getCallCount('clearPartitionedOriginDataAndCookies'));
    assertEquals(0, browserProxy.getCallCount('setOriginPermissions'));
    assertEquals(5, testElement.$.allSitesList.items![0].numCookies);
  });

  test('cancel remove site group', function() {
    testElement.siteGroupMap.set(
        TEST_MULTIPLE_SITE_GROUP.groupingKey,
        structuredClone(TEST_MULTIPLE_SITE_GROUP));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstSiteGroup();
    cancelDialog();

    assertEquals(0, browserProxy.getCallCount('setOriginPermissions'));
    assertEquals(1, testElement.$.allSitesList.items!.length);
    assertEquals(0, browserProxy.getCallCount('clearSiteGroupDataAndCookies'));
  });

  test('cancel remove origin', function() {
    const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
    siteGroup.origins[0]!.numCookies = 1;
    siteGroup.origins[1]!.numCookies = 2;
    siteGroup.origins[2]!.numCookies = 3;
    siteGroup.numCookies = 6;
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstOrigin();
    cancelDialog();

    assertEquals(
        0, browserProxy.getCallCount('clearUnpartitionedOriginDataAndCookies'));
    assertEquals(0, browserProxy.getCallCount('setOriginPermissions'));
    assertEquals(6, testElement.$.allSitesList.items![0].numCookies);
  });

  test('permissions bullet point visbility', function() {
    const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
    siteGroup.origins[0]!.hasPermissionSettings = true;
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstOrigin();
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    assertTrue(isChildVisible(testElement, '#permissionsBulletPoint'));
    cancelDialog();

    removeFirstSiteGroup();
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    assertTrue(isChildVisible(testElement, '#permissionsBulletPoint'));
    cancelDialog();

    siteGroup.origins[0]!.hasPermissionSettings = false;
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstOrigin();
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    assertFalse(isChildVisible(testElement, '#permissionsBulletPoint'));
    cancelDialog();

    removeFirstSiteGroup();
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    assertFalse(isChildVisible(testElement, '#permissionsBulletPoint'));
    cancelDialog();
  });

  test('dynamic strings', async function() {
    // Single origin, no apps.
    const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstOrigin();
    assertEquals(
        getSubstitutedString(
            'siteSettingsRemoveSiteOriginDialogTitle', 'subdomain.example.com'),
        testElement.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteTitle')!.innerText);
    assertEquals(
        getString('siteSettingsRemoveSiteOriginLogout'),
        testElement.shadowRoot!
            .querySelector<HTMLElement>('#logoutBulletPoint')!.innerText);
    cancelDialog();

    // Site group, multiple origins, no apps.
    removeFirstSiteGroup();
    assertEquals(
        getSubstitutedString(
            'siteSettingsRemoveSiteGroupDialogTitle', 'example.com'),
        testElement.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteTitle')!.innerText);
    assertEquals(
        getString('siteSettingsRemoveSiteGroupLogout'),
        testElement.shadowRoot!
            .querySelector<HTMLElement>('#logoutBulletPoint')!.innerText);
    cancelDialog();

    // Single origin with app.
    siteGroup.origins[0]!.isInstalled = true;
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstOrigin();
    assertEquals(
        getSubstitutedString(
            'siteSettingsRemoveSiteOriginAppDialogTitle',
            'subdomain.example.com'),
        testElement.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteTitle')!.innerText);
    assertEquals(
        getString('siteSettingsRemoveSiteOriginLogout'),
        testElement.shadowRoot!
            .querySelector<HTMLElement>('#logoutBulletPoint')!.innerText);
    cancelDialog();

    // Site group, multiple origins, with single app.
    removeFirstSiteGroup();
    assertEquals(
        getSubstitutedString(
            'siteSettingsRemoveSiteGroupAppDialogTitle', 'example.com'),
        testElement.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteTitle')!.innerText);
    assertEquals(
        getString('siteSettingsRemoveSiteGroupLogout'),
        testElement.shadowRoot!
            .querySelector<HTMLElement>('#logoutBulletPoint')!.innerText);
    cancelDialog();

    // Site group, multiple sites, multiple apps.
    siteGroup.origins[1]!.isInstalled = true;
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstSiteGroup();
    assertEquals(
        getSubstitutedString(
            'siteSettingsRemoveSiteGroupAppPluralDialogTitle', 'example.com'),
        testElement.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteTitle')!.innerText);
    assertEquals(
        getString('siteSettingsRemoveSiteGroupLogout'),
        testElement.shadowRoot!
            .querySelector<HTMLElement>('#logoutBulletPoint')!.innerText);
    cancelDialog();

    // Site group, single origin, no app.
    const singleOriginSiteGroup = structuredClone(TEST_SINGLE_SITE_GROUP);
    testElement.siteGroupMap.set(
        singleOriginSiteGroup.groupingKey,
        structuredClone(singleOriginSiteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstSiteGroup();
    assertEquals(
        getSubstitutedString(
            'siteSettingsRemoveSiteOriginDialogTitle', 'single.example.com'),
        testElement.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteTitle')!.innerText);
    assertEquals(
        getString('siteSettingsRemoveSiteOriginLogout'),
        testElement.shadowRoot!
            .querySelector<HTMLElement>('#logoutBulletPoint')!.innerText);
    cancelDialog();

    // Site group, single origin, one app.
    singleOriginSiteGroup.origins[0]!.isInstalled = true;
    testElement.siteGroupMap.set(
        singleOriginSiteGroup.groupingKey,
        structuredClone(singleOriginSiteGroup));
    testElement.forceListUpdateForTesting();
    flush();

    removeFirstSiteGroup();
    assertEquals(
        getSubstitutedString(
            'siteSettingsRemoveSiteOriginAppDialogTitle', 'single.example.com'),
        testElement.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteTitle')!.innerText);
    assertEquals(
        getString('siteSettingsRemoveSiteOriginLogout'),
        testElement.shadowRoot!
            .querySelector<HTMLElement>('#logoutBulletPoint')!.innerText);
  });
});

suite('EnableRelatedWebsiteSets', function() {
  /**
   * An example eTLD+1 Object with multiple origins grouped under it.
   */
  const TEST_MULTIPLE_SITE_GROUP =
      createSiteGroup('example.com', 'example.com', [
        'http://example.com',
        'https://www.example.com',
        'https://login.example.com',
      ]);

  /**
   * Example site groups with one owned SiteGroup.
   */
  const TEST_SITE_GROUPS: SiteGroup[] = [
    {
      groupingKey: groupingKey('foo.com'),
      etldPlus1: 'foo.com',
      displayName: 'foo.com',
      origins: [createOriginInfo('https://foo.com')],
      numCookies: 0,
      rwsOwner: 'foo.com',
      hasInstalledPWA: false,
    },
    {
      groupingKey: groupingKey('bar.com'),
      etldPlus1: 'bar.com',
      displayName: 'bar.com',
      origins: [createOriginInfo('https://bar.com')],
      numCookies: 0,
      hasInstalledPWA: false,
    },
    {
      groupingKey: groupingKey('example.com'),
      etldPlus1: 'example.com',
      displayName: 'example.com',
      origins: [createOriginInfo('https://example.com')],
      numCookies: 0,
      hasInstalledPWA: false,
    },
  ];

  /**
   * Example related website set site groups.
   */
  const TEST_RWS_SITE_GROUPS: SiteGroup[] = [
    {
      groupingKey: groupingKey('google.com'),
      etldPlus1: 'google.com',
      displayName: 'google.com',
      origins: [
        createOriginInfo('https://google.com'),
        createOriginInfo('https://translate.google.com'),
      ],
      numCookies: 4,
      rwsOwner: 'google.com',
      rwsNumMembers: 2,
      hasInstalledPWA: false,
    },
    {
      groupingKey: groupingKey('youtube.com'),
      etldPlus1: 'youtube.com',
      displayName: 'youtube.com',
      origins: [createOriginInfo('https://youtube.com')],
      numCookies: 0,
      rwsOwner: 'google.com',
      rwsNumMembers: 2,
      hasInstalledPWA: false,
    },
  ];

  let testElement: AllSitesElement;

  /**
   * The mock proxy object to use during test.
   */
  let browserProxy: TestSiteSettingsPrefsBrowserProxy;

  let metricsBrowserProxy: TestMetricsBrowserProxy;

  suiteSetup(function() {
    CrSettingsPrefs.setInitialized();

    loadTimeData.overrideValues({
      firstPartySetsUIEnabled: true,
    });
  });

  suiteTeardown(function() {
    CrSettingsPrefs.resetForTesting();
  });


  // Initialize a site-list before each test.
  setup(async function() {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;

    browserProxy = new TestSiteSettingsPrefsBrowserProxy();
    SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
    metricsBrowserProxy = new TestMetricsBrowserProxy();
    MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy);
    testElement = document.createElement('all-sites');
    assertTrue(!!testElement);
    document.body.appendChild(testElement);
  });

  teardown(function() {
    // The code being tested changes the Route. Reset so that state is not
    // leaked across tests.
    Router.getInstance().resetRouteForTesting();
  });

  function removeSiteViaOverflowMenu(buttonType: string) {
    assertTrue(
        buttonType === 'cancel-button' || buttonType === 'action-button');
    flush();
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertTrue(siteEntries.length >= 1);
    const overflowMenuButton =
        siteEntries[0]!.shadowRoot!.querySelector<HTMLElement>(
            '#rwsOverflowMenuButton')!;
    assertFalse(
        overflowMenuButton.closest<HTMLElement>('.row-aligned')!.hidden);

    // Test clicking on the overflow menu button opens the menu.
    const overflowMenu = testElement.$.menu.get();
    assertFalse(overflowMenu.open);
    overflowMenuButton.click();
    assertTrue(overflowMenu.open);
    flush();
    const menuItems =
        overflowMenu.querySelectorAll<HTMLElement>('.dropdown-item');
    assertFalse(testElement.$.confirmRemoveSite.get().open);
    menuItems[1]!.click();
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    const actionButtonList =
        testElement.$.confirmRemoveSite.get().querySelectorAll<HTMLElement>(
            `.${buttonType}`);
    assertEquals(1, actionButtonList.length);
    actionButtonList[0]!.click();
    // Check the dialog and overflow menu are now both closed.
    assertFalse(testElement.$.confirmRemoveSite.get().open);
    assertFalse(overflowMenu.open);
  }

  function removeFirstSiteGroup() {
    const siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(1, siteEntries.length);
    siteEntries[0]!.shadowRoot!.querySelector<HTMLElement>(
                                   '#removeSiteButton')!.click();
  }

  function confirmDialog() {
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    testElement.$.confirmRemoveSite.get()
        .querySelector<HTMLElement>('.action-button')!.click();
  }

  function cancelDialog() {
    assertTrue(testElement.$.confirmRemoveSite.get().open);
    testElement.$.confirmRemoveSite.get()
        .querySelector<HTMLElement>('.cancel-button')!.click();
  }

  test('remove site via overflow menu', async function() {
    const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
    siteGroup.rwsOwner = 'google.com';
    testElement.siteGroupMap.set(
        siteGroup.groupingKey, structuredClone(siteGroup));
    testElement.forceListUpdateForTesting();
    assertEquals(testElement.$.allSitesList.items!.length, 1);
    removeSiteViaOverflowMenu('action-button');
    assertEquals(testElement.$.allSitesList.items!.length, 0);

    assertEquals(
        DeleteBrowsingDataAction.SITES_SETTINGS_PAGE,
        await metricsBrowserProxy.whenCalled('recordDeleteBrowsingDataAction'));
  });

  test(
      'cancelling the confirm dialog on removing site works', async function() {
        const siteGroup = structuredClone(TEST_MULTIPLE_SITE_GROUP);
        siteGroup.rwsOwner = 'google.com';
        testElement.siteGroupMap.set(
            siteGroup.groupingKey, structuredClone(siteGroup));
        testElement.forceListUpdateForTesting();
        removeSiteViaOverflowMenu('cancel-button');
      });

  test('click and remove site entry with remove button', async function() {
    testElement.siteGroupMap.set(
        TEST_MULTIPLE_SITE_GROUP.groupingKey,
        structuredClone(TEST_MULTIPLE_SITE_GROUP));
    testElement.forceListUpdateForTesting();
    flush();
    removeFirstSiteGroup();
    confirmDialog();

    assertEquals(
        DeleteBrowsingDataAction.SITES_SETTINGS_PAGE,
        await metricsBrowserProxy.whenCalled('recordDeleteBrowsingDataAction'));
  });

  test(
      'click and cancel dialog site entry with remove button',
      async function() {
        testElement.siteGroupMap.set(
            TEST_MULTIPLE_SITE_GROUP.groupingKey,
            structuredClone(TEST_MULTIPLE_SITE_GROUP));
        testElement.forceListUpdateForTesting();
        flush();
        removeFirstSiteGroup();
        cancelDialog();
      });

  test('filter sites by related website set owner', async function() {
    TEST_SITE_GROUPS.forEach(siteGroup => {
      testElement.siteGroupMap.set(
          siteGroup.groupingKey, structuredClone(siteGroup));
    });
    testElement.forceListUpdateForTesting();
    flush();
    let siteEntries =
        testElement.$.listContainer.querySelectorAll('site-entry');
    assertEquals(3, siteEntries.length);
    const overflowMenuButton =
        siteEntries[0]!.shadowRoot!.querySelector<HTMLElement>(
            '#rwsOverflowMenuButton')!;
    assertFalse(
        overflowMenuButton.closest<HTMLElement>('.row-aligned')!.hidden);

    // Test clicking on the overflow menu button opens the menu.
    const overflowMenu = testElement.$.menu.get();
    assertFalse(overflowMenu.open);
    overflowMenuButton.click();
    assertTrue(overflowMenu.open);
    flush();
    const menuItems =
        overflowMenu.querySelectorAll<HTMLElement>('.dropdown-item');
    assertEquals('', testElement.filter);
    // Click show related sites.
    menuItems[0]!.click();
    // Check the overflow menu is now closed.
    assertFalse(overflowMenu.open);
    // Verify filter is applied in search query.
    assertEquals(
        'related:foo.com',
        Router.getInstance().getQueryParameters().get('searchSubpage'));
    // Filter needs to be set manually here as rerouting to all-sites with a
    // search query doesn't change it in this test.
    testElement.filter = 'related:foo.com';
    flush();

    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    let hiddenSiteEntries = Array.from(
        testElement.shadowRoot!.querySelectorAll('site-entry[hidden]'));
    assertEquals(1, siteEntries.length - hiddenSiteEntries.length);
    assertEquals('foo.com', siteEntries[0]!.siteGroup.rwsOwner);

    // Clear filter and assert the list is back to 3 elements.
    testElement.filter = '';
    flush();

    siteEntries = testElement.$.listContainer.querySelectorAll('site-entry');
    hiddenSiteEntries = Array.from(
        testElement.shadowRoot!.querySelectorAll('site-entry[hidden]'));
    assertEquals(3, siteEntries.length - hiddenSiteEntries.length);
  });

  test(
      'site entry related website set information updated on site deletion',
      async function() {
        TEST_RWS_SITE_GROUPS.forEach(siteGroup => {
          testElement.siteGroupMap.set(
              siteGroup.groupingKey, structuredClone(siteGroup));
        });
        testElement.forceListUpdateForTesting();
        flush();
        let siteEntries =
            testElement.$.listContainer.querySelectorAll('site-entry');
        assertEquals(testElement.$.allSitesList.items!.length, 2);
        await browserProxy.whenCalled('getRwsMembershipLabel');
        assertEquals(
            '· 2 sites in google.com\'s group',
            siteEntries[1]!.$.rwsMembership.innerText.trim());

        // Remove first site group.
        removeSiteViaOverflowMenu('action-button');
        siteEntries =
            testElement.$.listContainer.querySelectorAll('site-entry');
        assertEquals(testElement.$.allSitesList.items!.length, 1);
        await browserProxy.whenCalled('getRwsMembershipLabel');
        assertEquals(
            '· 1 site in google.com\'s group',
            siteEntries[1]!.$.rwsMembership.innerText.trim());
      });

  test(
      'site entry related website set constant member count on origin deletion',
      async function() {
        TEST_RWS_SITE_GROUPS.forEach(siteGroup => {
          testElement.siteGroupMap.set(
              siteGroup.groupingKey, structuredClone(siteGroup));
        });
        testElement.forceListUpdateForTesting();
        flush();

        let siteEntries =
            testElement.$.listContainer.querySelectorAll('site-entry');
        assertEquals(testElement.$.allSitesList.items!.length, 2);
        await browserProxy.whenCalled('getRwsMembershipLabel');
        assertEquals(
            '· 2 sites in google.com\'s group',
            siteEntries[1]!.$.rwsMembership.innerText.trim());

        let originList = siteEntries[0]!.$.originList.get();
        flush();
        // Ensure there are 2 origin entries.
        let originEntries = originList.querySelectorAll('.hr');
        assertEquals(2, originEntries.length);

        // Remove the first origin.
        originEntries[0]!.querySelector<HTMLElement>(
                             '#removeOriginButton')!.click();
        assertTrue(testElement.$.confirmRemoveSite.get().open);
        testElement.$.confirmRemoveSite.get()
            .querySelector<HTMLElement>('.action-button')!.click();

        // Validate that only 1 origin entry remaining.
        siteEntries =
            testElement.$.listContainer.querySelectorAll('site-entry');
        originList = siteEntries[0]!.$.originList.get();
        flush();
        originEntries = originList.querySelectorAll('.hr');
        assertEquals(1, originEntries.length);

        // Ensure that related website set info is unaffected by origin removal.
        await browserProxy.whenCalled('getRwsMembershipLabel');
        assertEquals(
            '· 2 sites in google.com\'s group',
            siteEntries[1]!.$.rwsMembership.innerText.trim());

        // Remove the last origin.
        siteEntries =
            testElement.$.listContainer.querySelectorAll('site-entry');
        originList = siteEntries[0]!.$.originList.get();
        flush();
        originEntries[0]!.querySelector<HTMLElement>(
                             '#removeOriginButton')!.click();
        assertTrue(testElement.$.confirmRemoveSite.get().open);
        testElement.$.confirmRemoveSite.get()
            .querySelector<HTMLElement>('.action-button')!.click();

        // Ensure that the site entry remains in the list as there are cookies
        // set at the eTLD+1 level so it converts to an ungrouped site entry and
        // related website set information remain unchanged.
        assertEquals(testElement.$.allSitesList.items!.length, 2);
        await browserProxy.whenCalled('getRwsMembershipLabel');
        assertEquals(
            '· 2 sites in google.com\'s group',
            siteEntries[1]!.$.rwsMembership.innerText.trim());
      });

  test(
      'show learn more about related website sets link when filtering by rws owner',
      function() {
        TEST_SITE_GROUPS.forEach(siteGroup => {
          testElement.siteGroupMap.set(
              siteGroup.groupingKey, structuredClone(siteGroup));
        });
        testElement.forceListUpdateForTesting();
        flush();
        let relatedWebsiteSetsLearnMore =
            testElement.shadowRoot!.querySelector<HTMLElement>('#relatedWebsiteSetsLearnMore');
        // When no filter is applied (as the test starts) the learn more link
        // should be hidden.
        assertTrue(relatedWebsiteSetsLearnMore!.hidden);

        testElement.filter = 'related:foo.com';
        flush();

        relatedWebsiteSetsLearnMore =
            testElement.shadowRoot!.querySelector<HTMLElement>('#relatedWebsiteSetsLearnMore');
        assertFalse(relatedWebsiteSetsLearnMore!.hidden);
        assertEquals(
            [
              loadTimeData.getStringF(
                  'siteSettingsRelatedWebsiteSetsLearnMore', 'foo.com'),
              loadTimeData.getString('learnMore'),
            ].join(' '),
            relatedWebsiteSetsLearnMore!.innerText.trim());

        testElement.filter = 'related:bar.com';
        flush();

        relatedWebsiteSetsLearnMore =
            testElement.shadowRoot!.querySelector<HTMLElement>('#relatedWebsiteSetsLearnMore');
        assertTrue(relatedWebsiteSetsLearnMore!.hidden);
      });
});