// 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.
/** @fileoverview Suite of tests for site-list. */
// clang-format off
import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import type {AddSiteDialogElement, SettingsEditExceptionDialogElement, SiteException, SiteListElement} from 'chrome://settings/lazy_load.js';
import {CookiesExceptionType, ContentSetting, ContentSettingsTypes, SITE_EXCEPTION_WILDCARD, SiteSettingSource, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
import {CrSettingsPrefs, loadTimeData, Router} from 'chrome://settings/settings.js';
import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {eventToPromise, microtasksFinished} 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, createRawSiteException, createSiteSettingsPrefs} from './test_util.js';
// clang-format on
/**
* An example pref with 2 blocked location items and 2 allowed. This pref
* is also used for the All Sites category and therefore needs values for
* all types, even though some might be blank.
*/
let prefsGeolocation: SiteSettingsPref;
/**
* An example pref that is empty.
*/
let prefsGeolocationEmpty: SiteSettingsPref;
/**
* An example pref with mixed schemes (present and absent).
*/
let prefsMixedSchemes: SiteSettingsPref;
/**
* An example pref with exceptions with origins and patterns from
* different providers.
*/
let prefsMixedProvider: SiteSettingsPref;
/**
* An example pref with with and without embeddingOrigin.
*/
let prefsMixedEmbeddingOrigin: SiteSettingsPref;
/**
* An example pref with file system write
*/
let prefsFileSystemWrite: SiteSettingsPref;
/**
* An example pref with multiple categories and multiple allow/block
* state.
*/
let prefsVarious: SiteSettingsPref;
/**
* An example pref with 1 allowed location item.
*/
let prefsOneEnabled: SiteSettingsPref;
/**
* An example pref with 1 blocked location item.
*/
let prefsOneDisabled: SiteSettingsPref;
/**
* An example pref with 1 allowed notification item.
*/
let prefsOneEnabledNotification: SiteSettingsPref;
/**
* An example pref with 1 blocked notification item.
*/
let prefsOneDisabledNotification: SiteSettingsPref;
/**
* An example pref with 2 allowed notification item.
*/
let prefsTwoEnabledNotification: SiteSettingsPref;
/**
* An example pref with 2 blocked notification item.
*/
let prefsTwoDisabledNotification: SiteSettingsPref;
/**
* An example Cookies pref with 1 in each of the three categories.
*/
let prefsSessionOnly: SiteSettingsPref;
/**
* An example Cookies pref with mixed incognito and regular settings.
*/
let prefsIncognito: SiteSettingsPref;
/**
* An example Javascript pref with a chrome-extension:// scheme.
*/
let prefsChromeExtension: SiteSettingsPref;
/**
* An example pref with 1 embargoed location item.
*/
let prefsEmbargo: SiteSettingsPref;
/**
* An example pref with Isolated Web App having notification.
*/
let prefsIsolatedWebApp: SiteSettingsPref;
/**
* An example pref with mixed cookies exception types: 2 exceptions with primary
* pattern wildcard, 2 exceptions with secondary pattern wildcard and 1
* exception with both patterns set.
*/
let prefsMixedCookiesExceptionTypes: SiteSettingsPref;
/**
* An example pref with mixed cookies exception types: 2 each for 1p allow, 1p
* block, 3p allow, and 3p block.
*/
let prefsMixedCookiesExceptionTypes2: SiteSettingsPref;
/**
* Creates all the test |SiteSettingsPref|s that are needed for the tests in
* this file. They are populated after test setup in order to access the
* |settings| constants required.
*/
function populateTestExceptions() {
prefsGeolocation = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.GEOLOCATION,
[
createRawSiteException('https://bar-allow.com:443'),
createRawSiteException('https://foo-allow.com:443'),
createRawSiteException('https://bar-block.com:443', {
setting: ContentSetting.BLOCK,
}),
createRawSiteException('https://foo-block.com:443', {
setting: ContentSetting.BLOCK,
}),
]),
]);
prefsMixedSchemes = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.GEOLOCATION,
[
createRawSiteException('https://foo-allow.com', {
source: SiteSettingSource.POLICY,
}),
createRawSiteException('bar-allow.com'),
]),
]);
prefsMixedProvider = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.GEOLOCATION,
[
createRawSiteException('https://[*.]foo.com', {
setting: ContentSetting.BLOCK,
source: SiteSettingSource.POLICY,
}),
createRawSiteException('https://bar.foo.com', {
setting: ContentSetting.BLOCK,
source: SiteSettingSource.POLICY,
}),
createRawSiteException('https://[*.]foo.com', {
setting: ContentSetting.BLOCK,
source: SiteSettingSource.POLICY,
}),
]),
]);
prefsMixedEmbeddingOrigin = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.IMAGES,
[
createRawSiteException('https://foo.com', {
embeddingOrigin: 'https://example.com',
}),
createRawSiteException('https://bar.com', {
embeddingOrigin: '',
}),
]),
]);
prefsVarious = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.GEOLOCATION,
[
createRawSiteException('https://foo.com', {
embeddingOrigin: '',
}),
createRawSiteException('https://bar.com', {
embeddingOrigin: '',
}),
]),
createContentSettingTypeToValuePair(
ContentSettingsTypes.NOTIFICATIONS,
[
createRawSiteException('https://google.com', {
embeddingOrigin: '',
}),
createRawSiteException('https://bar.com', {
embeddingOrigin: '',
}),
createRawSiteException('https://foo.com', {
embeddingOrigin: '',
}),
]),
]);
prefsOneEnabled = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.GEOLOCATION,
[createRawSiteException('https://foo-allow.com:443', {
embeddingOrigin: '',
type: ContentSettingsTypes.GEOLOCATION,
})]),
]);
prefsOneDisabled = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.GEOLOCATION,
[createRawSiteException('https://foo-block.com:443', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
})]),
]);
prefsOneEnabledNotification = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.NOTIFICATIONS,
[createRawSiteException('https://foo-allow.com:443', {
embeddingOrigin: '',
type: ContentSettingsTypes.NOTIFICATIONS,
setting: ContentSetting.ALLOW,
})]),
]);
prefsOneDisabledNotification = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.NOTIFICATIONS,
[createRawSiteException('https://foo-block.com:443', {
embeddingOrigin: '',
type: ContentSettingsTypes.NOTIFICATIONS,
setting: ContentSetting.BLOCK,
})]),
]);
prefsTwoEnabledNotification = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.NOTIFICATIONS,
[
createRawSiteException('https://foo-allow1.com:443', {
embeddingOrigin: '',
type: ContentSettingsTypes.NOTIFICATIONS,
setting: ContentSetting.ALLOW,
}),
createRawSiteException('https://foo-allow2.com:443', {
embeddingOrigin: '',
type: ContentSettingsTypes.NOTIFICATIONS,
setting: ContentSetting.ALLOW,
}),
]),
]);
prefsTwoDisabledNotification = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.NOTIFICATIONS,
[
createRawSiteException('https://foo-block1.com:443', {
embeddingOrigin: '',
type: ContentSettingsTypes.NOTIFICATIONS,
setting: ContentSetting.BLOCK,
}),
createRawSiteException('https://foo-block2.com:443', {
embeddingOrigin: '',
type: ContentSettingsTypes.NOTIFICATIONS,
setting: ContentSetting.BLOCK,
}),
]),
]);
prefsSessionOnly = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.COOKIES,
[
createRawSiteException('http://foo-block.com', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
}),
createRawSiteException('http://foo-allow.com', {
embeddingOrigin: '',
}),
createRawSiteException('http://foo-session.com', {
embeddingOrigin: '',
setting: ContentSetting.SESSION_ONLY,
}),
]),
]);
prefsIncognito = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.COOKIES,
[
// foo.com is blocked for regular sessions.
createRawSiteException('http://foo.com', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
}),
// bar.com is an allowed incognito item.
createRawSiteException('http://bar.com', {
embeddingOrigin: '',
incognito: true,
}),
// foo.com is allowed in incognito (overridden).
createRawSiteException('http://foo.com', {
embeddingOrigin: '',
incognito: true,
}),
]),
]);
prefsChromeExtension = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.JAVASCRIPT,
[createRawSiteException(
'chrome-extension://cfhgfbfpcbnnbibfphagcjmgjfjmojfa/', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
})]),
]);
prefsGeolocationEmpty = createSiteSettingsPrefs([], []);
prefsFileSystemWrite = createSiteSettingsPrefs(
[], [createContentSettingTypeToValuePair(
ContentSettingsTypes.FILE_SYSTEM_WRITE,
[createRawSiteException('http://foo.com', {
setting: ContentSetting.BLOCK,
})])]);
prefsEmbargo = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.GEOLOCATION,
[createRawSiteException('https://foo-block.com:443', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
isEmbargoed: true,
})]),
]);
const iwaOrigin = 'isolated-app://helloworldhelloworldhelloworldhe';
const nonIwaOrigin = 'https://bar.com';
prefsIsolatedWebApp = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.NOTIFICATIONS,
[
createRawSiteException(iwaOrigin, {
embeddingOrigin: '',
setting: ContentSetting.ALLOW,
displayName: iwaOrigin,
}),
createRawSiteException(nonIwaOrigin, {
embeddingOrigin: '',
displayName: nonIwaOrigin,
}),
]),
]);
prefsMixedCookiesExceptionTypes = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.COOKIES,
[
createRawSiteException('http://foo-block.com', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
}),
createRawSiteException('http://foo-allow.com', {
embeddingOrigin: '',
}),
createRawSiteException('http://bar-allow.com', {
embeddingOrigin: '',
}),
createRawSiteException('http://baz-allow.com', {
embeddingOrigin: '',
}),
createRawSiteException(SITE_EXCEPTION_WILDCARD, {
embeddingOrigin: 'http://3pc-block.com',
setting: ContentSetting.BLOCK,
}),
createRawSiteException(SITE_EXCEPTION_WILDCARD, {
embeddingOrigin: 'http://3pc-allow.com',
}),
createRawSiteException('http://mixed-primary-allow.com', {
embeddingOrigin: 'http://mixed-secondary-allow.com',
}),
]),
]);
prefsMixedCookiesExceptionTypes2 = createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
ContentSettingsTypes.COOKIES,
[
createRawSiteException('http://1p-foo-allow.com', {
embeddingOrigin: '',
}),
createRawSiteException('http://1p-bar-allow.com', {
embeddingOrigin: '',
}),
createRawSiteException('http://1p-foo-block.com', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
}),
createRawSiteException('http://1p-bar-block.com', {
embeddingOrigin: '',
setting: ContentSetting.BLOCK,
}),
createRawSiteException(SITE_EXCEPTION_WILDCARD, {
embeddingOrigin: 'http://3p-foo-allow.com',
}),
createRawSiteException(SITE_EXCEPTION_WILDCARD, {
embeddingOrigin: 'http://3p-bar-allow.com',
}),
createRawSiteException(SITE_EXCEPTION_WILDCARD, {
embeddingOrigin: 'http://3p-foo-block.com',
setting: ContentSetting.BLOCK,
}),
createRawSiteException(SITE_EXCEPTION_WILDCARD, {
embeddingOrigin: 'http://3p-bar-block.com',
setting: ContentSetting.BLOCK,
}),
]),
]);
}
suite('SiteListEmbargoedOrigin', function() {
/**
* A site list element created before each test.
*/
let testElement: SiteListElement;
/**
* The mock proxy object to use during test.
*/
let browserProxy: TestSiteSettingsPrefsBrowserProxy;
suiteSetup(function() {
CrSettingsPrefs.setInitialized();
});
suiteTeardown(function() {
CrSettingsPrefs.resetForTesting();
});
// Initialize a site-list before each test.
setup(function() {
populateTestExceptions();
browserProxy = new TestSiteSettingsPrefsBrowserProxy();
SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
testElement = document.createElement('site-list');
testElement.searchFilter = '';
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 for a particular category.
* @param category The category to set up.
* @param subtype Type of list to use.
* @param prefs The prefs to use.
*/
function setUpCategory(
category: ContentSettingsTypes, subtype: ContentSetting,
prefs: SiteSettingsPref) {
browserProxy.setPrefs(prefs);
testElement.categorySubtype = subtype;
testElement.category = category;
}
test('embargoed origin site description', async function() {
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.BLOCK, prefsEmbargo);
const result = await browserProxy.whenCalled('getExceptionList');
flush();
assertEquals(contentType, result);
// Validate that the sites gets populated from pre-canned prefs.
assertEquals(1, testElement.sites.length);
assertEquals(
prefsEmbargo.exceptions[contentType][0]!.origin,
testElement.sites[0]!.origin);
assertTrue(testElement.sites[0]!.isEmbargoed);
// Validate that embargoed site has correct subtitle.
assertEquals(
loadTimeData.getString('siteSettingsSourceEmbargo'),
testElement.$.listContainer.querySelectorAll('site-list-entry')[0]!
.shadowRoot!.querySelector('#siteDescription')!.innerHTML);
});
});
suite('SiteListCookiesExceptionTypes', function() {
/**
* A site list element created before each test.
*/
let testElement: SiteListElement;
/**
* The mock proxy object to use during test.
*/
let browserProxy: TestSiteSettingsPrefsBrowserProxy;
suiteSetup(function() {
CrSettingsPrefs.setInitialized();
});
suiteTeardown(function() {
CrSettingsPrefs.resetForTesting();
});
// Initialize a site-list before each test.
setup(function() {
populateTestExceptions();
browserProxy = new TestSiteSettingsPrefsBrowserProxy();
SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
testElement = document.createElement('site-list');
testElement.searchFilter = '';
document.body.appendChild(testElement);
});
/**
* Configures the test element for a particular category.
* @param category The category to set up.
* @param subtype Type of list to use.
* @param prefs The prefs to use.
*/
function setUpCategory(
category: ContentSettingsTypes, subtype: ContentSetting,
prefs: SiteSettingsPref) {
browserProxy.setPrefs(prefs);
testElement.categorySubtype = subtype;
testElement.category = category;
}
test('only shows third party cookies exceptions', async function() {
testElement.cookiesExceptionType = CookiesExceptionType.THIRD_PARTY;
setUpCategory(
ContentSettingsTypes.COOKIES, ContentSetting.ALLOW,
prefsMixedCookiesExceptionTypes);
await browserProxy.whenCalled('getExceptionList');
assertEquals(1, testElement.sites.length);
assertEquals(testElement.sites[0]!.embeddingOrigin, 'http://3pc-allow.com');
});
test('only shows site data cookies exceptions', async function() {
testElement.cookiesExceptionType = CookiesExceptionType.SITE_DATA;
setUpCategory(
ContentSettingsTypes.COOKIES, ContentSetting.ALLOW,
prefsMixedCookiesExceptionTypes);
await browserProxy.whenCalled('getExceptionList');
assertEquals(4, testElement.sites.length);
assertEquals(testElement.sites[0]!.origin, 'http://foo-allow.com');
assertEquals(testElement.sites[1]!.origin, 'http://bar-allow.com');
assertEquals(testElement.sites[2]!.origin, 'http://baz-allow.com');
assertEquals(
testElement.sites[3]!.origin, 'http://mixed-primary-allow.com');
assertEquals(
testElement.sites[3]!.embeddingOrigin,
'http://mixed-secondary-allow.com');
});
test('shows all cookies exceptions', async function() {
testElement.cookiesExceptionType = CookiesExceptionType.COMBINED;
setUpCategory(
ContentSettingsTypes.COOKIES, ContentSetting.ALLOW,
prefsMixedCookiesExceptionTypes);
await browserProxy.whenCalled('getExceptionList');
assertEquals(5, testElement.sites.length);
assertEquals(testElement.sites[0]!.origin, 'http://foo-allow.com');
assertEquals(testElement.sites[1]!.origin, 'http://bar-allow.com');
assertEquals(testElement.sites[2]!.origin, 'http://baz-allow.com');
assertEquals(testElement.sites[3]!.embeddingOrigin, 'http://3pc-allow.com');
assertEquals(
testElement.sites[4]!.origin, 'http://mixed-primary-allow.com');
assertEquals(
testElement.sites[4]!.embeddingOrigin,
'http://mixed-secondary-allow.com');
});
});
// TODO(crbug.com/929455, crbug.com/1064002): Flaky test. When it is fixed,
// merge SiteListDisabled back into SiteList.
suite('DISABLED_SiteList', function() {
/**
* A site list element created before each test.
*/
let testElement: SiteListElement;
/**
* The mock proxy object to use during test.
*/
let browserProxy: TestSiteSettingsPrefsBrowserProxy;
suiteSetup(function() {
// clang-format off
CrSettingsPrefs.setInitialized();
// clang-format on
});
suiteTeardown(function() {
CrSettingsPrefs.resetForTesting();
});
// Initialize a site-list before each test.
setup(function() {
populateTestExceptions();
browserProxy = new TestSiteSettingsPrefsBrowserProxy();
SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
testElement = document.createElement('site-list');
testElement.searchFilter = '';
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 for a particular category.
* @param category The category to set up.
* @param subtype Type of list to use.
* @param prefs The prefs to use.
*/
function setUpCategory(
category: ContentSettingsTypes, subtype: ContentSetting,
prefs: SiteSettingsPref) {
browserProxy.setPrefs(prefs);
testElement.cookiesExceptionType = CookiesExceptionType.COMBINED;
testElement.categorySubtype = subtype;
testElement.category = category;
}
test('list items shown and clickable when data is present', async function() {
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.ALLOW, prefsGeolocation);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
// Required for firstItem to be found below.
flush();
// Validate that the sites gets populated from pre-canned prefs.
assertEquals(2, testElement.sites.length);
assertEquals(
prefsGeolocation.exceptions[contentType][0]!.origin,
testElement.sites[0]!.origin);
assertEquals(
prefsGeolocation.exceptions[contentType][1]!.origin,
testElement.sites[1]!.origin);
// Validate that the sites are shown in UI and can be selected.
const clickable = testElement.shadowRoot!.querySelector('site-list-entry')!
.shadowRoot!.querySelector<HTMLElement>('.middle');
assertTrue(!!clickable);
clickable!.click();
await flushTasks();
assertEquals(
prefsGeolocation.exceptions[contentType][0]!.origin,
Router.getInstance().getQueryParameters().get('site'));
});
});
suite('SiteList', function() {
/**
* A site list element created before each test.
*/
let testElement: SiteListElement;
/**
* The mock proxy object to use during test.
*/
let browserProxy: TestSiteSettingsPrefsBrowserProxy;
suiteSetup(function() {
// clang-format off
CrSettingsPrefs.setInitialized();
// clang-format on
});
suiteTeardown(function() {
CrSettingsPrefs.resetForTesting();
});
// Initialize a site-list before each test.
setup(function() {
populateTestExceptions();
browserProxy = new TestSiteSettingsPrefsBrowserProxy();
SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
testElement = document.createElement('site-list');
testElement.searchFilter = '';
document.body.appendChild(testElement);
});
teardown(function() {
closeActionMenu();
// The code being tested changes the Route. Reset so that state is not
// leaked across tests.
Router.getInstance().resetRouteForTesting();
});
/**
* Opens the action menu for a particular element in the list.
* @param index The index of the child element (which site) to
* open the action menu for.
*/
function openActionMenu(index: number) {
const actionMenuButton =
testElement.$.listContainer.querySelectorAll('site-list-entry')[index]!
.$.actionMenuButton;
actionMenuButton.click();
flush();
}
/** Closes the action menu. */
function closeActionMenu() {
const menu = testElement.shadowRoot!.querySelector('cr-action-menu')!;
if (menu.open) {
menu.close();
}
}
/**
* Asserts the menu looks as expected.
* @param items The items expected to show in the menu.
*/
function assertMenu(items: string[]) {
const menu = testElement.shadowRoot!.querySelector('cr-action-menu');
assertTrue(!!menu);
const menuItems = menu!.querySelectorAll('button:not([hidden])');
assertEquals(items.length, menuItems.length);
for (let i = 0; i < items.length; i++) {
assertEquals(items[i], menuItems[i]!.textContent!.trim());
}
}
/**
* Configures the test element for a particular category.
* @param category The category to set up.
* @param subtype Type of list to use.
* @param prefs The prefs to use.
*/
function setUpCategory(
category: ContentSettingsTypes, subtype: ContentSetting,
prefs: SiteSettingsPref) {
browserProxy.setPrefs(prefs);
testElement.cookiesExceptionType = CookiesExceptionType.COMBINED;
testElement.categorySubtype = subtype;
testElement.category = category;
}
test('read-only attribute', async function() {
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW, prefsVarious);
await browserProxy.whenCalled('getExceptionList');
// Flush to be sure list container is populated.
flush();
const dotsMenu =
testElement.shadowRoot!.querySelector(
'site-list-entry')!.$.actionMenuButton;
assertFalse(dotsMenu.hidden);
testElement.toggleAttribute('read-only-list', true);
flush();
assertTrue(dotsMenu.hidden);
testElement.removeAttribute('read-only-list');
flush();
assertFalse(dotsMenu.hidden);
});
test('getExceptionList API used', async function() {
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsGeolocationEmpty);
const contentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(ContentSettingsTypes.GEOLOCATION, contentType);
});
/**
* Creates test |SiteSettingsPref|s with 2 allowed and 2 blocked
* sites for the given ContentSettingsTypes.
*/
function create2AllowAnd2BlockPrefs(type: ContentSettingsTypes) {
return createSiteSettingsPrefs([], [
createContentSettingTypeToValuePair(
type,
[
createRawSiteException('https://bar-allow.com:443'),
createRawSiteException('https://foo-allow.com:443'),
createRawSiteException('https://bar-block.com:443', {
setting: ContentSetting.BLOCK,
}),
createRawSiteException('https://foo-block.com:443', {
setting: ContentSetting.BLOCK,
}),
]),
]);
}
// Runs the system permission warning test for a given content type.
async function systemPermissionWarningTest(
category: ContentSettingsTypes, categoryName: string) {
setUpCategory(
category, ContentSetting.ALLOW, create2AllowAnd2BlockPrefs(category));
const contentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(category, contentType);
assertEquals(2, testElement.sites.length);
for (const disabled of [true, false]) {
const blockedPermissions = disabled ? [category] : [];
webUIListenerCallback('osGlobalPermissionChanged', blockedPermissions);
const warningElement =
testElement.$.category.querySelector<HTMLDivElement>(
'#systemPermissionDeclinedWarning');
assertTrue(!!warningElement);
const linkElement =
warningElement.querySelector('#openSystemSettingsLink');
if (!disabled) {
assertTrue(warningElement.hidden);
assertEquals(warningElement.textContent, '');
assertFalse(!!linkElement);
return;
}
assertFalse(warningElement.hidden);
assertEquals(
warningElement.textContent,
`These sites are allowed to use the ${categoryName},` +
` but ${categoryName} access is blocked on this device.` +
` Turn on ${categoryName} access.`);
assertTrue(!!linkElement);
assertEquals(linkElement.innerHTML, `Turn on ${categoryName} access.`);
linkElement.dispatchEvent(new MouseEvent('click'));
await browserProxy.whenCalled('openSystemPermissionSettings')
.then((contentType: string) => {
assertEquals(category, contentType);
});
}
}
test('System permission warning for camera', async function() {
await systemPermissionWarningTest(ContentSettingsTypes.CAMERA, 'camera');
});
test('System permission warning for microphone', async function() {
await systemPermissionWarningTest(ContentSettingsTypes.MIC, 'microphone');
});
test('System permission warning for location', async function() {
await systemPermissionWarningTest(
ContentSettingsTypes.GEOLOCATION, 'location');
});
test('Empty list', async function() {
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsGeolocationEmpty);
const contentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(ContentSettingsTypes.GEOLOCATION, contentType);
assertEquals(0, testElement.sites.length);
assertEquals(ContentSetting.ALLOW, testElement.categorySubtype);
assertFalse(testElement.$.category.hidden);
});
test('initial ALLOW state is correct', async function() {
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsGeolocation);
const contentType: ContentSettingsTypes =
await browserProxy.whenCalled('getExceptionList');
assertEquals(ContentSettingsTypes.GEOLOCATION, contentType);
assertEquals(2, testElement.sites.length);
assertEquals(
prefsGeolocation.exceptions[contentType][0]!.origin,
testElement.sites[0]!.origin);
assertEquals(
prefsGeolocation.exceptions[contentType][1]!.origin,
testElement.sites[1]!.origin);
assertEquals(ContentSetting.ALLOW, testElement.categorySubtype);
flush(); // Populates action menu.
openActionMenu(0);
assertMenu(['Block', 'Edit', 'Remove']);
assertFalse(testElement.$.category.hidden);
});
test('action menu closes when list changes', async function() {
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsGeolocation);
const actionMenu = testElement.shadowRoot!.querySelector('cr-action-menu')!;
await browserProxy.whenCalled('getExceptionList');
flush(); // Populates action menu.
openActionMenu(0);
assertTrue(actionMenu.open);
browserProxy.resetResolver('getExceptionList');
// Simulate a change in the underlying model.
webUIListenerCallback(
'contentSettingSitePermissionChanged',
ContentSettingsTypes.GEOLOCATION);
await browserProxy.whenCalled('getExceptionList');
// Check that the action menu was closed.
assertFalse(actionMenu.open);
});
test('exceptions are not reordered in non-ALL_SITES', async function() {
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.BLOCK,
prefsMixedProvider);
const contentType: ContentSettingsTypes =
await browserProxy.whenCalled('getExceptionList');
assertEquals(ContentSettingsTypes.GEOLOCATION, contentType);
assertEquals(3, testElement.sites.length);
for (let i = 0; i < testElement.sites.length; ++i) {
const exception = prefsMixedProvider.exceptions[contentType][i]!;
assertEquals(exception.origin, testElement.sites[i]!.origin);
let expectedControlledBy =
chrome.settingsPrivate.ControlledBy.PRIMARY_USER;
if (exception.source === SiteSettingSource.EXTENSION ||
exception.source === SiteSettingSource.HOSTED_APP) {
expectedControlledBy = chrome.settingsPrivate.ControlledBy.EXTENSION;
} else if (exception.source === SiteSettingSource.POLICY) {
expectedControlledBy = chrome.settingsPrivate.ControlledBy.USER_POLICY;
}
assertEquals(expectedControlledBy, testElement.sites[i]!.controlledBy);
}
});
test('initial BLOCK state is correct', function() {
const contentType = ContentSettingsTypes.GEOLOCATION;
const categorySubtype = ContentSetting.BLOCK;
setUpCategory(contentType, categorySubtype, prefsGeolocation);
return browserProxy.whenCalled('getExceptionList')
.then(function(actualContentType) {
assertEquals(contentType, actualContentType);
assertEquals(categorySubtype, testElement.categorySubtype);
assertEquals(2, testElement.sites.length);
assertEquals(
prefsGeolocation.exceptions[contentType][2]!.origin,
testElement.sites[0]!.origin);
assertEquals(
prefsGeolocation.exceptions[contentType][3]!.origin,
testElement.sites[1]!.origin);
flush(); // Populates action menu.
openActionMenu(0);
assertMenu(['Allow', 'Edit', 'Remove']);
assertFalse(testElement.$.category.hidden);
});
});
test('initial SESSION ONLY state is correct', function() {
const contentType = ContentSettingsTypes.COOKIES;
const categorySubtype = ContentSetting.SESSION_ONLY;
setUpCategory(contentType, categorySubtype, prefsSessionOnly);
return browserProxy.whenCalled('getExceptionList')
.then(function(actualContentType) {
assertEquals(contentType, actualContentType);
assertEquals(categorySubtype, testElement.categorySubtype);
assertEquals(1, testElement.sites.length);
assertEquals(
prefsSessionOnly.exceptions[contentType][2]!.origin,
testElement.sites[0]!.origin);
flush(); // Populates action menu.
openActionMenu(0);
assertMenu(['Allow', 'Block', 'Edit', 'Remove']);
assertFalse(testElement.$.category.hidden);
});
});
test('initial INCOGNITO BLOCK state is correct', async function() {
const contentType = ContentSettingsTypes.COOKIES;
const categorySubtype = ContentSetting.BLOCK;
setUpCategory(contentType, categorySubtype, prefsIncognito);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
assertEquals(categorySubtype, testElement.categorySubtype);
assertEquals(1, testElement.sites.length);
assertEquals(
prefsIncognito.exceptions[contentType][0]!.origin,
testElement.sites[0]!.origin);
flush(); // Populates action menu.
openActionMenu(0);
// 'Clear on exit' is visible as this is not an incognito item.
assertMenu(['Allow', 'Delete on exit', 'Edit', 'Remove']);
// Select 'Remove' from menu.
const remove = testElement.shadowRoot!.querySelector<HTMLElement>('#reset');
assertTrue(!!remove);
remove!.click();
const args =
await browserProxy.whenCalled('resetCategoryPermissionForPattern');
assertEquals('http://foo.com', args[0]);
assertEquals('', args[1]);
assertEquals(contentType, args[2]);
assertFalse(args[3]); // Incognito.
});
test('initial INCOGNITO ALLOW state is correct', async function() {
const contentType = ContentSettingsTypes.COOKIES;
const categorySubtype = ContentSetting.ALLOW;
setUpCategory(contentType, categorySubtype, prefsIncognito);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
assertEquals(categorySubtype, testElement.categorySubtype);
assertEquals(2, testElement.sites.length);
assertEquals(
prefsIncognito.exceptions[contentType][1]!.origin,
testElement.sites[0]!.origin);
assertEquals(
prefsIncognito.exceptions[contentType][2]!.origin,
testElement.sites[1]!.origin);
flush(); // Populates action menu.
openActionMenu(0);
// 'Clear on exit' is hidden for incognito items.
assertMenu(['Block', 'Edit', 'Remove']);
closeActionMenu();
// Select 'Remove' from menu on 'foo.com'.
openActionMenu(1);
const remove = testElement.shadowRoot!.querySelector<HTMLElement>('#reset');
assertTrue(!!remove);
remove!.click();
const args =
await browserProxy.whenCalled('resetCategoryPermissionForPattern');
assertEquals('http://foo.com', args[0]);
assertEquals('', args[1]);
assertEquals(contentType, args[2]);
assertTrue(args[3]); // Incognito.
});
test('reset button works for read-only content types', async function() {
testElement.readOnlyList = true;
flush();
const contentType = ContentSettingsTypes.GEOLOCATION;
const categorySubtype = ContentSetting.ALLOW;
setUpCategory(contentType, categorySubtype, prefsOneEnabled);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
assertEquals(categorySubtype, testElement.categorySubtype);
assertEquals(1, testElement.sites.length);
assertEquals(
prefsOneEnabled.exceptions[contentType][0]!.origin,
testElement.sites[0]!.origin);
flush();
const item = testElement.shadowRoot!.querySelector('site-list-entry')!;
// Assert action button is hidden.
const dots = item.$.actionMenuButton;
assertTrue(!!dots);
assertTrue(dots.hidden);
// Assert reset button is visible.
const resetButton =
item.shadowRoot!.querySelector<HTMLElement>('#resetSite');
assertTrue(!!resetButton);
assertFalse(resetButton!.hidden);
resetButton!.click();
const args =
await browserProxy.whenCalled('resetCategoryPermissionForPattern');
assertEquals('https://foo-allow.com:443', args[0]);
assertEquals('', args[1]);
assertEquals(contentType, args[2]);
});
test('edit action menu opens edit exception dialog', async function() {
setUpCategory(
ContentSettingsTypes.COOKIES, ContentSetting.SESSION_ONLY,
prefsSessionOnly);
await browserProxy.whenCalled('getExceptionList');
flush(); // Populates action menu.
openActionMenu(0);
assertMenu(['Allow', 'Block', 'Edit', 'Remove']);
const menu = testElement.shadowRoot!.querySelector('cr-action-menu')!;
assertTrue(menu.open);
const edit = testElement.shadowRoot!.querySelector<HTMLElement>('#edit');
assertTrue(!!edit);
edit!.click();
flush();
assertFalse(menu.open);
assertTrue(!!testElement.shadowRoot!.querySelector(
'settings-edit-exception-dialog'));
});
test('edit dialog closes when incognito status changes', async function() {
setUpCategory(
ContentSettingsTypes.COOKIES, ContentSetting.BLOCK, prefsSessionOnly);
await browserProxy.whenCalled('getExceptionList');
flush(); // Populates action menu.
openActionMenu(0);
testElement.shadowRoot!.querySelector<HTMLElement>('#edit')!.click();
flush();
const dialog =
testElement.shadowRoot!.querySelector('settings-edit-exception-dialog');
assertTrue(!!dialog);
const closeEventPromise = eventToPromise('close', dialog!);
browserProxy.setIncognito(true);
await closeEventPromise;
assertFalse(!!testElement.shadowRoot!.querySelector(
'settings-edit-exception-dialog'));
});
test('Block list open when Allow list is empty', async function() {
// Prefs: One item in Block list, nothing in Allow list.
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.BLOCK, prefsOneDisabled);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
await flushTasks();
assertFalse(testElement.$.category.hidden);
assertNotEquals(0, testElement.$.listContainer.offsetHeight);
});
test('Block list open when Allow list is not empty', async function() {
// Prefs: Items in both Block and Allow list.
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.BLOCK, prefsGeolocation);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
await flushTasks();
assertFalse(testElement.$.category.hidden);
assertNotEquals(0, testElement.$.listContainer.offsetHeight);
});
test('Allow list is always open (Block list empty)', async function() {
// Prefs: One item in Allow list, nothing in Block list.
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.ALLOW, prefsOneEnabled);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
await flushTasks();
assertFalse(testElement.$.category.hidden);
assertNotEquals(0, testElement.$.listContainer.offsetHeight);
});
test('Allow list is always open (Block list non-empty)', async function() {
// Prefs: Items in both Block and Allow list.
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.ALLOW, prefsGeolocation);
const actualContentType = await browserProxy.whenCalled('getExceptionList');
assertEquals(contentType, actualContentType);
await flushTasks();
assertFalse(testElement.$.category.hidden);
assertNotEquals(0, testElement.$.listContainer.offsetHeight);
});
test('Block list not hidden when empty', function() {
// Prefs: One item in Allow list, nothing in Block list.
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.BLOCK, prefsOneEnabled);
return browserProxy.whenCalled('getExceptionList')
.then(function(actualContentType) {
assertEquals(contentType, actualContentType);
assertFalse(testElement.$.category.hidden);
});
});
test('Allow list not hidden when empty', function() {
// Prefs: One item in Block list, nothing in Allow list.
const contentType = ContentSettingsTypes.GEOLOCATION;
setUpCategory(contentType, ContentSetting.ALLOW, prefsOneDisabled);
return browserProxy.whenCalled('getExceptionList')
.then(function(actualContentType) {
assertEquals(contentType, actualContentType);
assertFalse(testElement.$.category.hidden);
});
});
test('Mixed embeddingOrigin', async function() {
setUpCategory(
ContentSettingsTypes.IMAGES, ContentSetting.ALLOW,
prefsMixedEmbeddingOrigin);
await browserProxy.whenCalled('getExceptionList');
// Required for firstItem to be found below.
flush();
// Validate that embeddingOrigin sites cannot be edited.
const entries = testElement.shadowRoot!.querySelectorAll('site-list-entry');
const firstItem = entries[0]!;
assertTrue(firstItem.$.actionMenuButton.hidden);
assertFalse(
firstItem.shadowRoot!.querySelector<HTMLElement>('#resetSite')!.hidden);
// Validate that non-embeddingOrigin sites can be edited.
const secondItem = entries[1]!;
assertFalse(secondItem.$.actionMenuButton.hidden);
assertTrue(secondItem.shadowRoot!.querySelector<HTMLElement>(
'#resetSite')!.hidden);
});
test('Isolated Web Apps', async function() {
setUpCategory(
ContentSettingsTypes.NOTIFICATIONS, ContentSetting.ALLOW,
prefsIsolatedWebApp);
await browserProxy.whenCalled('getExceptionList');
// Required for firstItem to be found below.
flush();
// Validate that IWAs cannot be edited.
const entries = testElement.shadowRoot!.querySelectorAll('site-list-entry');
const firstItem = entries[0]!;
assertTrue(firstItem.$.actionMenuButton.hidden);
assertFalse(
firstItem.shadowRoot!.querySelector<HTMLElement>('#resetSite')!.hidden);
// Validate that IWA displays app name and not origin.
assertEquals(
firstItem.shadowRoot!.querySelector<HTMLElement>(
'.url-directionality')!.textContent!.trim(),
prefsIsolatedWebApp!.exceptions!.notifications[0]!.displayName);
// Validate that non-IWAs can be edited.
const secondItem = entries[1]!;
assertFalse(secondItem.$.actionMenuButton.hidden);
assertTrue(secondItem.shadowRoot!.querySelector<HTMLElement>(
'#resetSite')!.hidden);
// Validate that non-IWA displays the displayName (in most cases same as
// the origin).
assertEquals(
secondItem.shadowRoot!
.querySelector<HTMLElement>(
'.url-directionality')!.textContent!.trim(),
prefsIsolatedWebApp!.exceptions!.notifications[1]!.displayName);
});
test('Mixed schemes (present and absent)', async function() {
// Prefs: One item with scheme and one without.
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsMixedSchemes);
// No further checks needed. If this fails, it will hang the test.
await browserProxy.whenCalled('getExceptionList');
});
test('Select menu item', async function() {
// Test for error: "Cannot read property 'origin' of undefined".
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsGeolocation);
await browserProxy.whenCalled('getExceptionList');
flush();
openActionMenu(0);
const allow = testElement.shadowRoot!.querySelector<HTMLElement>('#allow');
assertTrue(!!allow);
allow!.click();
await browserProxy.whenCalled('setCategoryPermissionForPattern');
});
test('Chrome Extension scheme', async function() {
setUpCategory(
ContentSettingsTypes.JAVASCRIPT, ContentSetting.BLOCK,
prefsChromeExtension);
await browserProxy.whenCalled('getExceptionList');
flush();
openActionMenu(0);
assertMenu(['Allow', 'Edit', 'Remove']);
const allow = testElement.shadowRoot!.querySelector<HTMLElement>('#allow');
assertTrue(!!allow);
allow!.click();
const args =
await browserProxy.whenCalled('setCategoryPermissionForPattern');
assertEquals(
'chrome-extension://cfhgfbfpcbnnbibfphagcjmgjfjmojfa/', args[0]);
assertEquals('', args[1]);
assertEquals(ContentSettingsTypes.JAVASCRIPT, args[2]);
assertEquals(ContentSetting.ALLOW, args[3]);
});
test(
'show-tooltip event fires on entry show common tooltip',
async function() {
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsGeolocation);
await browserProxy.whenCalled('getExceptionList');
flush();
const entry =
testElement.$.listContainer.querySelector('site-list-entry')!;
const tooltip = testElement.$.tooltip;
const testsParams = [
['a', testElement, new MouseEvent('mouseleave')],
['b', testElement, new MouseEvent('click')],
['c', testElement, new Event('blur')],
['d', tooltip, new MouseEvent('mouseenter')],
];
for (const params of testsParams) {
const text = params[0] as string;
const eventTarget = params[1] as HTMLElement;
const event = params[2] as MouseEvent;
entry.fire('show-tooltip', {target: testElement, text});
await microtasksFinished();
assertFalse(tooltip.$.tooltip.hidden);
assertEquals(text, tooltip.innerHTML.trim());
eventTarget.dispatchEvent(event);
await microtasksFinished();
assertTrue(tooltip.$.tooltip.hidden);
}
});
test(
'Add site button is hidden for content settings that don\'t allow it',
async function() {
setUpCategory(
ContentSettingsTypes.FILE_SYSTEM_WRITE, ContentSetting.ALLOW,
prefsFileSystemWrite);
await browserProxy.whenCalled('getExceptionList');
flush();
assertTrue(testElement.$.addSite.hidden);
});
test('Reset the last entry moves focus', async function() {
setUpCategory(
ContentSettingsTypes.NOTIFICATIONS, ContentSetting.ALLOW,
prefsOneEnabledNotification);
await browserProxy.whenCalled('getExceptionList');
await microtasksFinished();
flush(); // Populates action menu.
openActionMenu(0);
await microtasksFinished();
// Select 'Remove' from menu.
const remove = testElement.shadowRoot!.querySelector<HTMLElement>('#reset');
assertTrue(!!remove);
remove.click();
await browserProxy.whenCalled('resetCategoryPermissionForPattern');
await microtasksFinished();
// Resetting the last element should move the focus to the list's header.
assertEquals(
testElement.$.listHeader, testElement.shadowRoot!.activeElement);
});
test('Block the last allowed entry moves focus', async function() {
setUpCategory(
ContentSettingsTypes.NOTIFICATIONS, ContentSetting.ALLOW,
prefsOneEnabledNotification);
await browserProxy.whenCalled('getExceptionList');
await microtasksFinished();
flush(); // Populates action menu.
openActionMenu(0);
await microtasksFinished();
// Select 'block' from menu.
const block = testElement.shadowRoot!.querySelector<HTMLElement>('#block');
assertTrue(!!block);
block.click();
await browserProxy.whenCalled('setCategoryPermissionForPattern');
await microtasksFinished();
// Resetting the last element should move the focus to the list's header.
assertEquals(
testElement.$.listHeader, testElement.shadowRoot!.activeElement);
});
test('Allow the last blocked entry moves focus', async function() {
setUpCategory(
ContentSettingsTypes.NOTIFICATIONS, ContentSetting.BLOCK,
prefsOneDisabledNotification);
await browserProxy.whenCalled('getExceptionList');
await microtasksFinished();
flush(); // Populates action menu.
openActionMenu(0);
await microtasksFinished();
// Select 'allow' from menu.
const allow = testElement.shadowRoot!.querySelector<HTMLElement>('#allow');
assertTrue(!!allow);
allow.click();
await browserProxy.whenCalled('setCategoryPermissionForPattern');
await microtasksFinished();
// Resetting the last element should move the focus to the list's header.
assertEquals(
testElement.$.listHeader, testElement.shadowRoot!.activeElement);
});
test('Reset not the last entry focuses the next entry', async function() {
setUpCategory(
ContentSettingsTypes.NOTIFICATIONS, ContentSetting.ALLOW,
prefsTwoEnabledNotification);
await browserProxy.whenCalled('getExceptionList');
await microtasksFinished();
flush(); // Populates action menu.
openActionMenu(0);
await microtasksFinished();
// Select 'Remove' from menu.
const remove = testElement.shadowRoot!.querySelector<HTMLElement>('#reset');
assertTrue(!!remove);
remove.click();
await browserProxy.whenCalled('resetCategoryPermissionForPattern');
await microtasksFinished();
const firstListEntry =
testElement.$.listContainer.querySelectorAll('site-list-entry')[0];
assertTrue(!!firstListEntry);
// Focus a site’s list entry.
assertEquals(firstListEntry, testElement.shadowRoot!.activeElement);
});
test(
'Block not the last allowed entry focuses the next entry',
async function() {
setUpCategory(
ContentSettingsTypes.NOTIFICATIONS, ContentSetting.ALLOW,
prefsTwoEnabledNotification);
await browserProxy.whenCalled('getExceptionList');
await microtasksFinished();
flush(); // Populates action menu.
openActionMenu(0);
await microtasksFinished();
// Select 'block' from menu.
const block =
testElement.shadowRoot!.querySelector<HTMLElement>('#block');
assertTrue(!!block);
block.click();
await browserProxy.whenCalled('setCategoryPermissionForPattern');
await microtasksFinished();
const firstListEntry =
testElement.$.listContainer.querySelectorAll('site-list-entry')[0];
assertTrue(!!firstListEntry);
// Focus a site’s list entry.
assertEquals(firstListEntry, testElement.shadowRoot!.activeElement);
});
test(
'Allow not the last blocked entry focuses the next entry',
async function() {
setUpCategory(
ContentSettingsTypes.NOTIFICATIONS, ContentSetting.BLOCK,
prefsTwoDisabledNotification);
await browserProxy.whenCalled('getExceptionList');
await microtasksFinished();
flush(); // Populates action menu.
openActionMenu(0);
await microtasksFinished();
// Select 'allow' from menu.
const allow =
testElement.shadowRoot!.querySelector<HTMLElement>('#allow');
assertTrue(!!allow);
allow.click();
await browserProxy.whenCalled('setCategoryPermissionForPattern');
await microtasksFinished();
const firstListEntry =
testElement.$.listContainer.querySelectorAll('site-list-entry')[0];
assertTrue(!!firstListEntry);
// Focus a site’s list entry.
assertEquals(firstListEntry, testElement.shadowRoot!.activeElement);
});
test('Reset the last Geolocation entry moves focus', async function() {
testElement.readOnlyList = true;
flush();
setUpCategory(
ContentSettingsTypes.GEOLOCATION, ContentSetting.ALLOW,
prefsOneEnabled);
await browserProxy.whenCalled('getExceptionList');
flush();
const item = testElement.shadowRoot!.querySelector('site-list-entry')!;
const resetButton =
item.shadowRoot!.querySelector<HTMLElement>('#resetSite');
assertTrue(!!resetButton);
resetButton.click();
await browserProxy.whenCalled('resetCategoryPermissionForPattern');
await microtasksFinished();
// Resetting the last element should move the focus to the list's header.
assertEquals(
testElement.$.listHeader, testElement.shadowRoot!.activeElement);
});
});
suite('SiteListSearchTests', function() {
/** A site list element created before each test. */
let testElement: SiteListElement;
/** The mock proxy object to use during test. */
let browserProxy: TestSiteSettingsPrefsBrowserProxy;
suiteSetup(function() {
CrSettingsPrefs.setInitialized();
});
suiteTeardown(function() {
CrSettingsPrefs.resetForTesting();
});
// Initialize a site-list before each test.
setup(function() {
populateTestExceptions();
browserProxy = new TestSiteSettingsPrefsBrowserProxy();
SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
testElement = document.createElement('site-list');
document.body.appendChild(testElement);
});
/**
* Configures the test element for a particular category.
* @param category The category to set up.
* @param subtype Type of list to use.
* @param prefs The prefs to use.
*/
function setUpCategory(
category: ContentSettingsTypes, subtype: ContentSetting,
prefs: SiteSettingsPref) {
browserProxy.setPrefs(prefs);
testElement.categorySubtype = subtype;
testElement.category = category;
}
test('no search lists all 1p and 3p allow exceptions', async function() {
testElement.cookiesExceptionType = CookiesExceptionType.COMBINED;
testElement.searchFilter = '';
setUpCategory(
ContentSettingsTypes.COOKIES, ContentSetting.ALLOW,
prefsMixedCookiesExceptionTypes2);
await browserProxy.whenCalled('getExceptionList');
flush();
// The mock data contains 4 allow exceptions.
assertEquals(
4,
testElement.$.listContainer.querySelectorAll('site-list-entry').length);
});
test('search lists matching 1p and 3p allow exceptions', async function() {
testElement.cookiesExceptionType = CookiesExceptionType.COMBINED;
testElement.searchFilter = 'foo';
setUpCategory(
ContentSettingsTypes.COOKIES, ContentSetting.ALLOW,
prefsMixedCookiesExceptionTypes2);
await browserProxy.whenCalled('getExceptionList');
flush();
// The mock data contains 2 foo allow exceptions.
assertEquals(
2,
testElement.$.listContainer.querySelectorAll('site-list-entry').length);
});
});
suite('EditExceptionDialog', function() {
let dialog: SettingsEditExceptionDialogElement;
/**
* The dialog tests don't call |getExceptionList| so the exception needs to
* be processed as a |SiteException|.
*/
let cookieException: SiteException;
let browserProxy: TestSiteSettingsPrefsBrowserProxy;
setup(function() {
cookieException = {
category: ContentSettingsTypes.COOKIES,
embeddingOrigin: SITE_EXCEPTION_WILDCARD,
isEmbargoed: false,
incognito: false,
setting: ContentSetting.BLOCK,
enforcement: null,
controlledBy: chrome.settingsPrivate.ControlledBy.USER_POLICY,
displayName: 'foo.com',
origin: 'foo.com',
description: '',
};
browserProxy = new TestSiteSettingsPrefsBrowserProxy();
SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
dialog = document.createElement('settings-edit-exception-dialog');
dialog.model = cookieException;
document.body.appendChild(dialog);
});
teardown(function() {
dialog.remove();
});
test('invalid input', async function() {
const input = dialog.shadowRoot!.querySelector('cr-input');
assertTrue(!!input);
assertFalse(input!.invalid);
const actionButton = dialog.$.actionButton;
assertTrue(!!actionButton);
assertFalse(actionButton.disabled);
// Simulate user input of whitespace only text.
input!.value = ' ';
await input.updateComplete;
input!.dispatchEvent(
new CustomEvent('input', {bubbles: true, composed: true}));
flush();
assertTrue(actionButton.disabled);
assertTrue(input!.invalid);
// Simulate user input of invalid text.
browserProxy.setIsPatternValidForType(false);
const expectedPattern = '*';
input!.value = expectedPattern;
await input.updateComplete;
input!.dispatchEvent(
new CustomEvent('input', {bubbles: true, composed: true}));
const [pattern, _category] =
await browserProxy.whenCalled('isPatternValidForType');
assertEquals(expectedPattern, pattern);
assertTrue(actionButton.disabled);
assertTrue(input!.invalid);
});
test('action button calls proxy', async function() {
const input = dialog.shadowRoot!.querySelector('cr-input');
assertTrue(!!input);
// Simulate user edit.
const newValue = input!.value + ':1234';
input!.value = newValue;
await input.updateComplete;
const actionButton = dialog.$.actionButton;
assertTrue(!!actionButton);
assertFalse(actionButton.disabled);
actionButton.click();
const args1 =
await browserProxy.whenCalled('resetCategoryPermissionForPattern');
assertEquals(cookieException.origin, args1[0]);
assertEquals(cookieException.embeddingOrigin, args1[1]);
assertEquals(ContentSettingsTypes.COOKIES, args1[2]);
assertEquals(cookieException.incognito, args1[3]);
const args2 =
await browserProxy.whenCalled('setCategoryPermissionForPattern');
assertEquals(newValue, args2[0]);
assertEquals(SITE_EXCEPTION_WILDCARD, args2[1]);
assertEquals(ContentSettingsTypes.COOKIES, args2[2]);
assertEquals(cookieException.setting, args2[3]);
assertEquals(cookieException.incognito, args2[4]);
assertFalse(dialog.$.dialog.open);
});
});
suite('AddExceptionDialog', function() {
let dialog: AddSiteDialogElement;
let browserProxy: TestSiteSettingsPrefsBrowserProxy;
async function inputText(expectedPattern: string) {
const actionButton = dialog.$.add;
assertTrue(!!actionButton);
assertTrue(actionButton.disabled);
const input = dialog.shadowRoot!.querySelector('cr-input');
assertTrue(!!input);
input.value = expectedPattern;
await input.updateComplete;
input.dispatchEvent(
new CustomEvent('input', {bubbles: true, composed: true}));
const [pattern, _category] =
await browserProxy.whenCalled('isPatternValidForType');
assertEquals(expectedPattern, pattern);
assertFalse(actionButton.disabled);
}
setup(function() {
populateTestExceptions();
browserProxy = new TestSiteSettingsPrefsBrowserProxy();
SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
dialog = document.createElement('add-site-dialog');
dialog.category = ContentSettingsTypes.GEOLOCATION;
dialog.contentSetting = ContentSetting.ALLOW;
dialog.hasIncognito = false;
document.body.appendChild(dialog);
});
teardown(function() {
dialog.remove();
});
test('incognito', function() {
dialog.set('hasIncognito', true);
flush();
assertFalse(dialog.$.incognito.checked);
dialog.$.incognito.checked = true;
// Changing the incognito status will reset the checkbox.
dialog.set('hasIncognito', false);
flush();
assertFalse(dialog.$.incognito.checked);
});
test('invalid input', async function() {
// Initially the action button should be disabled, but the error warning
// should not be shown for an empty input.
const input = dialog.shadowRoot!.querySelector('cr-input');
assertTrue(!!input);
assertFalse(input!.invalid);
const actionButton = dialog.$.add;
assertTrue(!!actionButton);
assertTrue(actionButton.disabled);
// Simulate user input of invalid text.
browserProxy.setIsPatternValidForType(false);
const expectedPattern = 'foobarbaz';
input!.value = expectedPattern;
await input.updateComplete;
input!.dispatchEvent(
new CustomEvent('input', {bubbles: true, composed: true}));
const [pattern, _category] =
await browserProxy.whenCalled('isPatternValidForType');
assertEquals(expectedPattern, pattern);
assertTrue(actionButton.disabled);
assertTrue(input!.invalid);
});
test(
'add cookie exception for combined cookie exception type',
async function() {
dialog.set('category', ContentSettingsTypes.COOKIES);
dialog.set('cookiesExceptionType', CookiesExceptionType.COMBINED);
flush();
// Enter a pattern and click the button.
const expectedPattern = 'foo-bar.com';
await inputText(expectedPattern);
dialog.$.add.click();
// The created exception has secondary pattern wildcard
// (created site data cookie exception).
const [primaryPattern, secondaryPattern] =
await browserProxy.whenCalled('setCategoryPermissionForPattern');
assertEquals(primaryPattern, expectedPattern);
assertEquals(secondaryPattern, SITE_EXCEPTION_WILDCARD);
});
test('add third party cookie exception', async function() {
dialog.set('category', ContentSettingsTypes.COOKIES);
dialog.set('cookiesExceptionType', CookiesExceptionType.THIRD_PARTY);
flush();
// Enter a pattern and click the button.
const expectedPattern = 'foo-bar.com';
await inputText(expectedPattern);
dialog.$.add.click();
// The created exception has primary pattern wildcard (third party
// exception).
const [primaryPattern, secondaryPattern] =
await browserProxy.whenCalled('setCategoryPermissionForPattern');
assertEquals(primaryPattern, SITE_EXCEPTION_WILDCARD);
assertEquals(secondaryPattern, expectedPattern);
});
test('add site data cookie exception', async function() {
dialog.set('category', ContentSettingsTypes.COOKIES);
dialog.set('cookiesExceptionType', CookiesExceptionType.SITE_DATA);
flush();
// Enter a pattern and click the button.
const expectedPattern = 'foo-bar.com';
await inputText(expectedPattern);
dialog.$.add.click();
// The created exception has secondary pattern wildcard (site data
// exception).
const [primaryPattern, secondaryPattern] =
await browserProxy.whenCalled('setCategoryPermissionForPattern');
assertEquals(primaryPattern, expectedPattern);
assertEquals(secondaryPattern, SITE_EXCEPTION_WILDCARD);
});
test('add tracking protection exception', async function() {
dialog.set('category', ContentSettingsTypes.TRACKING_PROTECTION);
flush();
// Enter a pattern and click the button.
const expectedPattern = 'foo-bar.com';
await inputText(expectedPattern);
dialog.$.add.click();
// The created exception has primary pattern wildcard.
const [primaryPattern, secondaryPattern] =
await browserProxy.whenCalled('setCategoryPermissionForPattern');
assertEquals(primaryPattern, SITE_EXCEPTION_WILDCARD);
assertEquals(secondaryPattern, expectedPattern);
});
});