blob: ed2a2aeb9fe1ee8eae52c6234fa62ef8093740a5 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import type {ElementHandle} from 'puppeteer-core';
import {navigateToNetworkTab, setTextFilter, waitForSomeRequestsToAppear} from '../helpers/network-helpers.js';
import {openPanelViaMoreTools} from '../helpers/settings-helpers.js';
import type {DevToolsPage} from '../shared/frontend-helper.js';
import type {InspectedPage} from '../shared/target-helper.js';
async function checkboxIsChecked(element: ElementHandle<HTMLInputElement>): Promise<boolean> {
return await element.evaluate(node => node.checked);
}
async function setCheckBox(element: ElementHandle<HTMLInputElement>, wantChecked: boolean): Promise<void> {
const checked = await checkboxIsChecked(element);
if (checked !== wantChecked) {
await element.click();
}
assert.strictEqual(await checkboxIsChecked(element), wantChecked);
}
async function isVisible(element: ElementHandle, container: ElementHandle): Promise<boolean> {
const elementBox = JSON.parse(await element.evaluate(e => JSON.stringify(e.getBoundingClientRect())));
const containerBox = JSON.parse(await container.evaluate(e => JSON.stringify(e.getBoundingClientRect())));
return elementBox.top <= containerBox.top ? containerBox.top - elementBox.top <= elementBox.height :
elementBox.bottom - containerBox.bottom <= elementBox.height;
}
async function setupRequestBlocking(
devToolsPage: DevToolsPage,
{patterns, enabled, useURLPatterns}: {patterns: string[], enabled: boolean, useURLPatterns: boolean}):
Promise<void> {
await openPanelViaMoreTools(useURLPatterns ? 'Request conditions' : 'Network request blocking', devToolsPage);
for (const pattern of patterns) {
await devToolsPage.click(
useURLPatterns ? 'aria/Add network request blocking or throttling pattern' :
'aria/Add network request blocking pattern');
await devToolsPage.click('.blocked-url-edit-value > input');
await devToolsPage.typeText(useURLPatterns ? `:*/*${pattern}` : pattern);
await devToolsPage.click('aria/Add');
}
const networkRequestBlockingCheckbox =
await (await devToolsPage.waitForAria(
useURLPatterns ? 'Enable blocking and throttling' : 'Enable network request blocking'))
.toElement('input');
await setCheckBox(networkRequestBlockingCheckbox, enabled);
}
async function disableRequestBlocking(devToolsPage: DevToolsPage, useURLPatterns: boolean): Promise<void> {
await openPanelViaMoreTools(useURLPatterns ? 'Request conditions' : 'Network request blocking', devToolsPage);
await devToolsPage.click(
useURLPatterns ? 'aria/Remove all network request blocking or throttling patterns' :
'aria/Remove all network request blocking patterns');
const networkRequestBlockingCheckbox =
await (await devToolsPage.waitForAria(
useURLPatterns ? 'Enable blocking and throttling' : 'Enable network request blocking'))
.toElement('input');
await setCheckBox(networkRequestBlockingCheckbox, false);
}
for (const useURLPatterns of [true]) {
describe(`The ${useURLPatterns ? 'Request conditions drawer' : 'Network request blocking panel'}`, function() {
if (this.timeout() > 0) {
this.timeout(20000);
}
async function setUpHostConfig(devToolsPage: DevToolsPage) {
if (useURLPatterns) {
const hostConfig = {
devToolsIndividualRequestThrottling: {enabled: true},
};
await devToolsPage.evaluateOnNewDocument(`
Object.defineProperty(window, 'InspectorFrontendHost', {
configurable: true,
enumerable: true,
get() {
return this._InspectorFrontendHost;
},
set(value) {
value.getHostConfig = (cb) => {
cb({
...globalThis.hostConfigForTesting ?? {},
...JSON.parse('${JSON.stringify(hostConfig)}'),
});
}
this._InspectorFrontendHost = value;
}
});
`);
await devToolsPage.reload({
waitUntil: 'networkidle0',
});
}
}
it('prohibits unchecking patterns when blocking is disabled', async ({devToolsPage}) => {
await setUpHostConfig(devToolsPage);
await setupRequestBlocking(
devToolsPage, {patterns: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], enabled: false, useURLPatterns});
await devToolsPage.waitForAriaNone('Edit');
await devToolsPage.waitForAriaNone('Remove');
const firstListItem = await devToolsPage.waitFor('.blocked-url');
const firstCheckbox =
await (await devToolsPage.waitFor('.widget > .list > .list-item > .blocked-url > .blocked-url-checkbox'))
.toElement('input');
assert.isTrue(await checkboxIsChecked(firstCheckbox));
await firstListItem.click();
assert.isTrue(await checkboxIsChecked(firstCheckbox));
await disableRequestBlocking(devToolsPage, useURLPatterns);
});
it('allows scrolling the pattern list when blocking is disabled', async ({devToolsPage}) => {
await setUpHostConfig(devToolsPage);
await setupRequestBlocking(
devToolsPage, {patterns: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], enabled: false, useURLPatterns});
const list = await devToolsPage.waitFor('.list');
const lastListItem = await devToolsPage.waitForElementWithTextContent(useURLPatterns ? '*://*:*/*9' : '9');
// TODO: this is not completely fair way to scroll but mouseWheel does not
// seem to work here in the new-headless on Windows and Linux.
await lastListItem.scrollIntoView();
await devToolsPage.waitForFunction(() => isVisible(lastListItem, list));
await disableRequestBlocking(devToolsPage, useURLPatterns);
});
it('displays blocked reason for CSP blocked requests', async ({devToolsPage, inspectedPage}) => {
await setUpHostConfig(devToolsPage);
await navigateToNetworkTab('csp.html', devToolsPage, inspectedPage);
await setTextFilter('csp.js', devToolsPage);
await inspectedPage.evaluate(() => {
// @ts-expect-error
sendCSPRequest();
});
await waitForSomeRequestsToAppear(1, devToolsPage);
const status = await devToolsPage.waitFor('.network-log-grid tbody .status-column');
assert.strictEqual(await status.evaluate(node => node.textContent), '(blocked:csp)');
await disableRequestBlocking(devToolsPage, useURLPatterns);
});
async function testBlockedURL(
patterns: string[], url: string, expectedStatus: string,
wrappers: {devToolsPage: DevToolsPage, inspectedPage: InspectedPage}) {
const {devToolsPage, inspectedPage} = wrappers;
await setUpHostConfig(devToolsPage);
await setupRequestBlocking(devToolsPage, {patterns, enabled: true, useURLPatterns});
await navigateToNetworkTab('csp.html', devToolsPage, inspectedPage);
await setTextFilter(url.substring(url.lastIndexOf('/') + 1), devToolsPage);
inspectedPage.evaluate(url => {
// @ts-expect-error
addBlockedScript(url);
}, url);
await waitForSomeRequestsToAppear(1, devToolsPage);
const status = await devToolsPage.waitFor('.network-log-grid tbody .status-column');
assert.strictEqual(await status.evaluate(node => node.textContent), expectedStatus);
await disableRequestBlocking(devToolsPage, useURLPatterns);
}
it('displays blocked reason for DevTools blocked requests matching with stars inside the pattern',
testBlockedURL.bind(null, ['resources**/silent*.js'], 'silent_script.js', '(blocked:devtools)'));
it('does not display blocked reason for requests non-matching due to the different component order',
testBlockedURL.bind(null, ['x*y'], 'yx', '404Not Found'));
it('displays blocked reason for DevTools blocked requests matching with stars around the pattern',
testBlockedURL.bind(null, ['**pattern**'], 'there/is/a/pattern/inside.js', '(blocked:devtools)'));
it('does not display blocked reason for requests non-matching due to an extra character',
testBlockedURL.bind(null, ['pattern'], 'patt1ern', '404Not Found'));
it('does not display blocked reason for requests non-matching due to a different component',
testBlockedURL.bind(null, ['*this***is*a*pattern'], 'file/this/is/the/pattern', '404Not Found'));
it('displays blocked reason for DevTools blocked requests matching with multiple components',
testBlockedURL.bind(null, ['*this***is*a*pattern'], 'this/is/a/pattern', '(blocked:devtools)'));
it('does not display blocked reason for requests non-matching due to a missing component',
testBlockedURL.bind(null, ['*this***is*a*pattern'], 'this/is', '404Not Found'));
it('displays blocked reason for DevTools blocked requests matching with a simple substring',
testBlockedURL.bind(null, ['pattern*'], 'long/pattern/inside', '(blocked:devtools)'));
it('displays blocked reason for DevTools blocked requests matching the pattern exactly',
testBlockedURL.bind(null, ['pattern'], 'pattern', '(blocked:devtools)'));
it('displays blocked reason for DevTools blocked requests matching duplicate pattern',
testBlockedURL.bind(null, ['pattern', 'pattern'], 'pattern', '(blocked:devtools)'));
it('does not display blocked reason for requests non-matching due to a missing repeated acomponent',
testBlockedURL.bind(null, ['v*w*x*y*z'], 'zyxwvzyxwvzyxwvzyxwv', '404Not Found'));
it('displays blocked reason for DevTools blocked requests matching with multiple repeated components',
testBlockedURL.bind(null, ['v*w*x*y*z'], 'zyxwvzyxwvzyxwvzyxwvz', '(blocked:devtools)'));
it('displays blocked reason for DevTools blocked requests matching all patterns',
testBlockedURL.bind(null, ['one1', 'two2'], 'one1two2', '(blocked:devtools)'));
it('does not display blocked reason for requests non-matching any of the patterns',
testBlockedURL.bind(null, ['one1', 'two2', 'three3'], 'four4', '404Not Found'));
it('displays blocked reason for DevTools blocked requests matching some patterns',
testBlockedURL.bind(null, ['one1*', 'two2*', 'three3*'], 'only-two2-here', '(blocked:devtools)'));
});
}