| // Copyright 2021 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 * as puppeteer from 'puppeteer-core'; |
| |
| import {expectError} from '../../conductor/events.js'; |
| import { |
| CONSOLE_TAB_SELECTOR, |
| focusConsolePrompt, |
| } from '../helpers/console-helpers.js'; |
| import { |
| clickInfobarButton, |
| getAllRequestNames, |
| getTextFromHeadersRow, |
| navigateToNetworkTab, |
| selectRequestByName, |
| waitForSomeRequestsToAppear, |
| } from '../helpers/network-helpers.js'; |
| import type {DevToolsPage} from '../shared/frontend-helper.js'; |
| import type {InspectedPage} from '../shared/target-helper.js'; |
| |
| const SIMPLE_PAGE_REQUEST_NUMBER = 2; |
| const SIMPLE_PAGE_URL = `requests.html?num=${SIMPLE_PAGE_REQUEST_NUMBER}`; |
| |
| const configureAndCheckHeaderOverrides = async (devToolsPage: DevToolsPage, inspectedPage: InspectedPage) => { |
| await clickInfobarButton(devToolsPage); |
| let networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('#tab-headers-component', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('#tab-headers-component.tabbed-pane-header-tab[aria-selected=true]', networkView); |
| let responseHeaderSection = await devToolsPage.waitFor('[aria-label="Response headers"]', networkView); |
| |
| let row = await devToolsPage.waitFor('.row', responseHeaderSection); |
| assert.deepEqual(await getTextFromHeadersRow(row, devToolsPage), [ |
| 'cache-control', |
| 'max-age=3600', |
| ]); |
| |
| await devToolsPage.waitForFunction(async () => { |
| await devToolsPage.click('.header-name', {root: row}); |
| await devToolsPage.click('.header-value', {root: row}); |
| await devToolsPage.pasteText('Foo'); |
| return (await getTextFromHeadersRow(row, devToolsPage))[1] === 'Foo'; |
| }); |
| |
| await devToolsPage.click('[title="Reveal header override definitions"]'); |
| |
| const headersView = await devToolsPage.waitFor('devtools-sources-headers-view'); |
| const headersViewRow = await devToolsPage.waitFor('.row.padded', headersView); |
| assert.deepEqual(await getTextFromHeadersRow(headersViewRow, devToolsPage), [ |
| 'cache-control', |
| 'Foo', |
| ]); |
| |
| await navigateToNetworkTab('hello.html', devToolsPage, inspectedPage); |
| await selectRequestByName('hello.html', {devToolsPage}); |
| networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('#tab-headers-component', { |
| root: networkView, |
| }); |
| |
| responseHeaderSection = await devToolsPage.waitFor('[aria-label="Response headers"]'); |
| row = await devToolsPage.waitFor('.row.header-overridden', responseHeaderSection); |
| assert.deepEqual(await getTextFromHeadersRow(row, devToolsPage), ['cache-control', 'Foo']); |
| }; |
| |
| describe('The Network Request view', () => { |
| it('re-opens the same tab after switching to another panel and navigating back to the "Network" tab (https://crbug.com/1184578)', |
| async ({ |
| devToolsPage, |
| inspectedPage, |
| }) => { |
| await navigateToNetworkTab(SIMPLE_PAGE_URL, devToolsPage, inspectedPage); |
| |
| await waitForSomeRequestsToAppear(SIMPLE_PAGE_REQUEST_NUMBER + 1, devToolsPage); |
| |
| await selectRequestByName('image.svg?id=0', {devToolsPage}); |
| let networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.clickMoreTabsButton(networkView); |
| await devToolsPage.click('[aria-label=Timing]'); |
| await devToolsPage.waitFor('[aria-label=Timing].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| |
| await devToolsPage.click(CONSOLE_TAB_SELECTOR); |
| await focusConsolePrompt(devToolsPage); |
| |
| await devToolsPage.click('#tab-network'); |
| await devToolsPage.waitFor('.network-log-grid'); |
| |
| networkView = await devToolsPage.waitFor('.network-item-view'); |
| const selectedTabHeader = await devToolsPage.waitFor('.tabbed-pane-header-tab[aria-selected=true]', networkView); |
| const selectedTabText = await selectedTabHeader.evaluate(element => element.textContent || ''); |
| |
| assert.strictEqual(selectedTabText, 'Timing'); |
| }); |
| |
| it('prevents requests on the preview tab.', async ({ |
| devToolsPage, |
| inspectedPage, |
| }) => { |
| await navigateToNetworkTab('embedded_requests.html', devToolsPage, inspectedPage); |
| |
| // For the issue to manifest it's mandatory to load the stylesheet by absolute URL. A relative URL would be treated |
| // relative to the data URL in the preview iframe and thus not work. We need to generate the URL because the |
| // resources path is dynamic, but we can't have any scripts in the resource page since they would be disabled in the |
| // preview. Therefore, the resource page contains just an iframe and we're filling it dynamically with content here. |
| const stylesheet = `${inspectedPage.getResourcesPath()}/network/style.css`; |
| const contents = `<head><link rel="stylesheet" href="${stylesheet}"></head><body><p>Content</p></body>`; |
| await inspectedPage.waitForFunction(async () => (await inspectedPage.page.$('iframe')) ?? undefined); |
| const dataUrl = `data:text/html,${contents}`; |
| await inspectedPage.evaluate((dataUrl: string) => { |
| (document.querySelector('iframe') as HTMLIFrameElement).src = dataUrl; |
| }, dataUrl); |
| |
| await waitForSomeRequestsToAppear(3, devToolsPage); |
| |
| const names = await getAllRequestNames(devToolsPage); |
| const name = names.find(v => v?.startsWith('data:')); |
| assert.isOk(name); |
| await selectRequestByName(name, {devToolsPage}); |
| |
| const styleSrcError = expectError(`Loading the stylesheet '${stylesheet}' violates`); |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=Preview].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Preview].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| |
| const frame = await devToolsPage.waitFor('.html-preview-frame'); |
| const content = await devToolsPage.waitForFunction(async () => (await frame.contentFrame()) ?? undefined); |
| const p = await devToolsPage.waitForFunction(async () => (await content.$('p')) ?? undefined); |
| |
| const color = await p.evaluate(e => getComputedStyle(e).color); |
| |
| assert.deepEqual(color, 'rgb(0, 0, 0)'); |
| await devToolsPage.waitForFunction(async () => styleSrcError.caught); |
| }); |
| |
| it('permits inline styles on the preview tab.', async ({devToolsPage, inspectedPage}) => { |
| await navigateToNetworkTab('embedded_requests.html', devToolsPage, inspectedPage); |
| const contents = '<head><style>p { color: red; }</style></head><body><p>Content</p></body>'; |
| await devToolsPage.waitForFunction(async () => (await inspectedPage.page.$('iframe')) ?? undefined); |
| const dataUrl = `data:text/html,${contents}`; |
| await inspectedPage.evaluate((dataUrl: string) => { |
| (document.querySelector('iframe') as HTMLIFrameElement).src = dataUrl; |
| }, dataUrl); |
| |
| await waitForSomeRequestsToAppear(2, devToolsPage); |
| |
| const name = await devToolsPage.waitForFunction(async () => { |
| const names = await getAllRequestNames(devToolsPage); |
| const name = names.find(v => v?.startsWith('data:')); |
| return name; |
| }); |
| await selectRequestByName(name, {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=Preview].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Preview].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| |
| const frame = await devToolsPage.waitFor('.html-preview-frame'); |
| const content = await devToolsPage.waitForFunction(async () => (await frame.contentFrame()) ?? undefined); |
| const p = await devToolsPage.waitForFunction(async () => (await content.$('p')) ?? undefined); |
| |
| const color = await p.evaluate(e => getComputedStyle(e).color); |
| |
| assert.deepEqual(color, 'rgb(255, 0, 0)'); |
| }); |
| |
| const navigateToEventStreamMessages = async (devToolsPage: DevToolsPage, inspectedPage: InspectedPage) => { |
| await navigateToNetworkTab('eventstream.html', devToolsPage, inspectedPage); |
| await waitForSomeRequestsToAppear(2, devToolsPage); |
| |
| await selectRequestByName('event-stream.rawresponse', {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=EventStream].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=EventStream].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| return await devToolsPage.waitFor('.event-source-messages-view'); |
| }; |
| |
| interface EventSourceMessageRaw { |
| id: string|undefined; |
| type: string|undefined; |
| data: string|undefined; |
| time?: string; |
| } |
| |
| const waitForMessages = async ( |
| messagesView: puppeteer.ElementHandle<Element>, |
| count: number, |
| devToolsPage: DevToolsPage, |
| ) => { |
| return await devToolsPage.waitForFunction(async () => { |
| const messages = await devToolsPage.$$('.data-grid-data-grid-node', messagesView); |
| if (messages.length !== count) { |
| return undefined; |
| } |
| |
| return await Promise.all(messages.map(message => { |
| return new Promise<EventSourceMessageRaw>(async resolve => { |
| const [id, type, data] = await Promise.all([ |
| devToolsPage.getTextContent('.id-column', message), |
| devToolsPage.getTextContent('.type-column', message), |
| devToolsPage.getTextContent('.data-column', message), |
| ]); |
| resolve({ |
| id, |
| type, |
| data, |
| }); |
| }); |
| })); |
| }); |
| }; |
| |
| const knownMessages: EventSourceMessageRaw[] = [ |
| {id: '1', type: 'custom-one', data: '{"one": "value-one"}'}, |
| {id: '2', type: 'message', data: '{"two": "value-two"}'}, |
| {id: '3', type: 'message', data: '{"three": "value-three"}'}, |
| ]; |
| const assertMessage = (actualMessage: EventSourceMessageRaw, expectedMessage: EventSourceMessageRaw) => { |
| assert.deepEqual(actualMessage.id, expectedMessage.id); |
| assert.deepEqual(actualMessage.type, expectedMessage.type); |
| assert.deepEqual(actualMessage.data, expectedMessage.data); |
| }; |
| const assertBaseState = async ( |
| messagesView: puppeteer.ElementHandle<Element>, |
| devToolsPage: DevToolsPage, |
| ) => { |
| const messages = await waitForMessages(messagesView, 3, devToolsPage); |
| assertMessage(messages[0], knownMessages[0]); |
| assertMessage(messages[1], knownMessages[1]); |
| assertMessage(messages[2], knownMessages[2]); |
| }; |
| |
| it('stores EventSource filter', async ({devToolsPage, inspectedPage}) => { |
| const messagesView = await navigateToEventStreamMessages(devToolsPage, inspectedPage); |
| let messages = await waitForMessages(messagesView, 3, devToolsPage); |
| await assertBaseState(messagesView, devToolsPage); |
| |
| const inputSelector = '[aria-placeholder="Filter using regex (example: https?)'; |
| |
| const filterInput = await devToolsPage.waitFor(inputSelector, messagesView); |
| |
| // "one" |
| await filterInput.focus(); |
| await devToolsPage.typeText('one'); |
| messages = await waitForMessages(messagesView, 1, devToolsPage); |
| assertMessage(messages[0], knownMessages[0]); |
| |
| // clear |
| await devToolsPage.click('[title="Clear"]', { |
| root: messagesView, |
| }); |
| await assertBaseState(messagesView, devToolsPage); |
| |
| // "two" |
| await filterInput.focus(); |
| await devToolsPage.typeText('two'); |
| messages = await waitForMessages(messagesView, 1, devToolsPage); |
| assertMessage(messages[0], knownMessages[1]); |
| |
| // invalid regex |
| await filterInput.focus(); |
| await devToolsPage.typeText('invalid('); |
| messages = await waitForMessages(messagesView, 0, devToolsPage); |
| }); |
| |
| it('handles EventSource clear', async ({devToolsPage, inspectedPage}) => { |
| const messagesView = await navigateToEventStreamMessages(devToolsPage, inspectedPage); |
| await assertBaseState(messagesView, devToolsPage); |
| |
| await devToolsPage.click('[aria-label="Clear all"]', { |
| root: messagesView, |
| }); |
| await waitForMessages(messagesView, 0, devToolsPage); |
| }); |
| |
| it('stores websocket filter', async ({devToolsPage, inspectedPage}) => { |
| const navigateToWebsocketMessages = async (devToolsPage: DevToolsPage, inspectedPage: InspectedPage) => { |
| await navigateToNetworkTab('websocket.html', devToolsPage, inspectedPage); |
| |
| await waitForSomeRequestsToAppear(2, devToolsPage); |
| |
| await selectRequestByName('localhost', {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=Messages].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Messages].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| return await devToolsPage.waitFor('.resource-chunk-view'); |
| }; |
| |
| let messagesView = await navigateToWebsocketMessages(devToolsPage, inspectedPage); |
| const waitForMessages = async (count: number, devToolsPage: DevToolsPage) => { |
| return await devToolsPage.waitForFunction(async () => { |
| const messages = await devToolsPage.$$('.data-column.resource-chunk-view-td', messagesView); |
| if (messages.length !== count) { |
| return undefined; |
| } |
| return await Promise.all(messages.map(message => { |
| return message.evaluate(message => message.textContent || ''); |
| })); |
| }); |
| }; |
| let messages = await waitForMessages(4, devToolsPage); |
| |
| const filterInput = await devToolsPage.waitFor( |
| '[aria-label="Filter using regex (example: (web)?socket)"][role=textbox]', messagesView); |
| await filterInput.focus(); |
| await devToolsPage.typeText('p[ai]ng'); |
| |
| messages = await waitForMessages(2, devToolsPage); |
| assert.deepEqual(messages, ['ping', 'ping']); |
| |
| messagesView = await navigateToWebsocketMessages(devToolsPage, inspectedPage); |
| messages = await waitForMessages(2, devToolsPage); |
| |
| assert.deepEqual(messages, ['ping', 'ping']); |
| }); |
| |
| function assertOutlineMatches(expectedPatterns: string[], outline: string[]) { |
| const regexpSpecialChars = /[-\/\\^$*+?.()|[\]{}]/g; |
| for (const item of outline) { |
| const expectedPattern = expectedPatterns.shift(); |
| if (expectedPattern) { |
| assert.match(item, new RegExp(expectedPattern.replace(regexpSpecialChars, '\\$&').replace(/%/g, '.*'))); |
| } else { |
| assert.fail('Unexpected text: ' + item); |
| } |
| } |
| } |
| |
| it('shows request headers and payload', async ({devToolsPage, inspectedPage}) => { |
| await navigateToNetworkTab('headers-and-payload.html', devToolsPage, inspectedPage); |
| |
| await waitForSomeRequestsToAppear(2, devToolsPage); |
| |
| await selectRequestByName('image.svg?id=42¶m=a%20b', {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=Headers].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Headers].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| const expectedHeadersContent = [ |
| { |
| aria: 'General', |
| rows: [ |
| 'Request URL', |
| 'https://localhost:%/test/e2e/resources/network/image.svg?id=42¶m=a%20b', |
| 'Request Method', |
| 'POST', |
| 'Status Code', |
| '200 OK', |
| 'Remote Address', |
| '[::1]:%', |
| 'Referrer Policy', |
| 'strict-origin-when-cross-origin', |
| ], |
| }, |
| { |
| aria: 'Response headers', |
| rows: [ |
| 'cache-control', |
| 'max-age=%', |
| 'connection', |
| 'keep-alive', |
| 'content-type', |
| 'image/svg+xml; charset=utf-8', |
| 'date', |
| '%', |
| 'keep-alive', |
| 'timeout=5', |
| 'transfer-encoding', |
| 'chunked', |
| 'vary', |
| 'Origin', |
| ], |
| }, |
| { |
| aria: 'Request Headers', |
| rows: [ |
| 'accept', |
| '*/*', |
| 'accept-encoding', |
| '%', |
| 'accept-language', |
| '%', |
| 'connection', |
| 'keep-alive', |
| 'content-length', |
| '32', |
| 'content-type', |
| 'application/x-www-form-urlencoded;charset=UTF-8', |
| 'host', |
| 'localhost:%', |
| 'origin', |
| 'https://localhost:%', |
| 'referer', |
| 'https://localhost:%/test/e2e/resources/network/headers-and-payload.html', |
| 'sec-ch-ua', |
| '%', |
| 'sec-ch-ua-mobile', |
| '?0', |
| 'sec-ch-ua-platform', |
| '%', |
| 'sec-fetch-dest', |
| 'empty', |
| 'sec-fetch-mode', |
| 'cors', |
| 'sec-fetch-site', |
| 'same-origin', |
| 'user-agent', |
| 'Mozilla/5.0 %', |
| 'x-same-domain', |
| '1', |
| ], |
| }, |
| ]; |
| |
| for (const sectionContent of expectedHeadersContent) { |
| const section = await devToolsPage.waitFor(`[aria-label="${sectionContent.aria}"]`); |
| const rows = await devToolsPage.$$('.row', section); |
| const rowsText = |
| await (await Promise.all(rows.map(async row => await getTextFromHeadersRow(row, devToolsPage)))).flat(); |
| assertOutlineMatches(sectionContent.rows, rowsText); |
| } |
| |
| await devToolsPage.click('[aria-label=Payload].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Payload].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| const payloadView = await devToolsPage.waitFor('.request-payload-view'); |
| const payloadOutline = await devToolsPage.$$('[role=treeitem]:not(.hidden)', payloadView); |
| const payloadOutlineText = |
| await Promise.all(payloadOutline.map(async item => await item.evaluate(el => el.textContent || ''))); |
| const expectedPayloadContent = [ |
| 'Query String Parameters (2)View sourceView URL-encoded', |
| ['id42', 'parama b'], |
| 'Form Data (4)View sourceView URL-encoded', |
| ['fooalpha', 'barbeta:42:0', 'baz', '(empty)'], |
| ].flat(); |
| |
| assertOutlineMatches(expectedPayloadContent, payloadOutlineText); |
| |
| // Context menu to copy single parsed entry. |
| const parsedEntry = await devToolsPage.waitForElementWithTextContent('alpha'); |
| await parsedEntry.click({button: 'right'}); |
| await (await devToolsPage.waitForElementWithTextContent('Copy value')).click(); |
| assert.strictEqual(await devToolsPage.readClipboard(), 'alpha'); |
| |
| // Context menu to copy the raw payload. |
| const viewSource = await devToolsPage.waitForElementWithTextContent('View source'); |
| await viewSource.click(); |
| const source = await devToolsPage.waitForElementWithTextContent('id=42¶m=a%20b'); |
| await source.click({button: 'right'}); |
| await (await devToolsPage.waitForElementWithTextContent('Copy')).click(); |
| assert.strictEqual(await devToolsPage.readClipboard(), 'id=42¶m=a%20b'); |
| }); |
| |
| it('shows raw headers', async ({devToolsPage, inspectedPage}) => { |
| await navigateToNetworkTab('headers-and-payload.html', devToolsPage, inspectedPage); |
| |
| await waitForSomeRequestsToAppear(2, devToolsPage); |
| |
| await selectRequestByName('image.svg?id=42¶m=a%20b', {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=Headers].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Headers].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| const section = await devToolsPage.waitFor('[aria-label="Response headers"]'); |
| await devToolsPage.click('input', { |
| root: section, |
| }); |
| |
| const expectedRawHeadersContent = [ |
| 'HTTP/1.1 200 OK', |
| 'Content-Type: image/svg+xml; charset=utf-8', |
| 'Cache-Control: max-age=%', |
| 'Vary: Origin', |
| 'Date: %', |
| 'Connection: keep-alive', |
| 'Keep-Alive: timeout=5', |
| 'Transfer-Encoding: chunked', |
| ].join('\r\n'); |
| const rawHeaders = await devToolsPage.waitFor('.raw-headers', section); |
| const rawHeadersText = await rawHeaders.evaluate(el => el.textContent || ''); |
| assertOutlineMatches([expectedRawHeadersContent], [rawHeadersText]); |
| |
| const expectedHeadersContent = [ |
| { |
| aria: 'General', |
| rows: [ |
| 'Request URL', |
| 'https://localhost:%/test/e2e/resources/network/image.svg?id=42¶m=a%20b', |
| 'Request Method', |
| 'POST', |
| 'Status Code', |
| '200 OK', |
| 'Remote Address', |
| '[::1]:%', |
| 'Referrer Policy', |
| 'strict-origin-when-cross-origin', |
| ], |
| }, |
| { |
| aria: 'Request Headers', |
| rows: [ |
| 'accept', |
| '*/*', |
| 'accept-encoding', |
| '%', |
| 'accept-language', |
| '%', |
| 'connection', |
| 'keep-alive', |
| 'content-length', |
| '32', |
| 'content-type', |
| 'application/x-www-form-urlencoded;charset=UTF-8', |
| 'host', |
| 'localhost:%', |
| 'origin', |
| 'https://localhost:%', |
| 'referer', |
| 'https://localhost:%/test/e2e/resources/network/headers-and-payload.html', |
| 'sec-ch-ua', |
| '%', |
| 'sec-ch-ua-mobile', |
| '?0', |
| 'sec-ch-ua-platform', |
| '%', |
| 'sec-fetch-dest', |
| 'empty', |
| 'sec-fetch-mode', |
| 'cors', |
| 'sec-fetch-site', |
| 'same-origin', |
| 'user-agent', |
| 'Mozilla/5.0 %', |
| 'x-same-domain', |
| '1', |
| ], |
| }, |
| ]; |
| |
| for (const sectionContent of expectedHeadersContent) { |
| const section = await devToolsPage.waitFor(`[aria-label="${sectionContent.aria}"]`); |
| const rows = await devToolsPage.$$('.row', section); |
| const rowsText = |
| await (await Promise.all(rows.map(async row => await getTextFromHeadersRow(row, devToolsPage)))).flat(); |
| assertOutlineMatches(sectionContent.rows, rowsText); |
| } |
| }); |
| |
| it('payload tab selection is preserved', async ({devToolsPage, inspectedPage}) => { |
| await navigateToNetworkTab('headers-and-payload.html', devToolsPage, inspectedPage); |
| |
| await waitForSomeRequestsToAppear(3, devToolsPage); |
| |
| await selectRequestByName('image.svg?id=42¶m=a%20b', {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=Payload].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Payload].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| |
| await selectRequestByName('image.svg', {devToolsPage}); |
| await devToolsPage.waitForElementWithTextContent('foogamma'); |
| }); |
| |
| it('no duplicate payload tab on headers update', async ({devToolsPage, inspectedPage}) => { |
| await navigateToNetworkTab('requests.html', devToolsPage, inspectedPage); |
| void inspectedPage.evaluate(() => fetch('image.svg?delay')); |
| await waitForSomeRequestsToAppear(2, devToolsPage); |
| |
| await selectRequestByName('image.svg?delay', {devToolsPage}); |
| await inspectedPage.evaluate(async () => await fetch('/?send_delayed')); |
| }); |
| |
| it('can create header overrides via request\'s context menu', async ({devToolsPage, inspectedPage}) => { |
| await devToolsPage.setupOverridesFSMocks(); |
| await devToolsPage.useSoftMenu(); |
| await navigateToNetworkTab('hello.html', devToolsPage, inspectedPage); |
| await selectRequestByName( |
| 'hello.html', |
| {button: 'right', devToolsPage}, |
| ); |
| |
| await devToolsPage.click('aria/Override headers'); |
| |
| await configureAndCheckHeaderOverrides(devToolsPage, inspectedPage); |
| }); |
| |
| it('can create header overrides via header\'s pencil icon', async ({devToolsPage, inspectedPage}) => { |
| await devToolsPage.setupOverridesFSMocks(); |
| await navigateToNetworkTab('hello.html', devToolsPage, inspectedPage); |
| await selectRequestByName('hello.html', {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('#tab-headers-component', { |
| root: networkView, |
| }); |
| |
| await devToolsPage.click('devtools-button.enable-editing'); |
| await configureAndCheckHeaderOverrides(devToolsPage, inspectedPage); |
| }); |
| |
| it('can search by headers name', async ({devToolsPage, inspectedPage}) => { |
| await navigateToNetworkTab('headers-and-payload.html', devToolsPage, inspectedPage); |
| |
| await waitForSomeRequestsToAppear(2, devToolsPage); |
| |
| await selectRequestByName('image.svg?id=42¶m=a%20b', {devToolsPage}); |
| const SEARCH_QUERY = '[aria-label="Find"]'; |
| const SEARCH_RESULT = '.search-result'; |
| |
| await devToolsPage.pressKey('f', {control: true}); |
| await devToolsPage.waitFor(SEARCH_QUERY); |
| const inputElement = await devToolsPage.$(SEARCH_QUERY); |
| assert.isOk(inputElement, 'Unable to find search input field'); |
| |
| await inputElement.focus(); |
| await inputElement.type('Cache-Control'); |
| await devToolsPage.page.keyboard.press('Enter'); |
| |
| await devToolsPage.waitFor(SEARCH_RESULT); |
| }); |
| |
| describe('preview', () => { |
| async function searchInPreview(devToolsPage: DevToolsPage, query: string, totalMatches: number) { |
| const SEARCH_QUERY = '.search-replace.search'; |
| |
| await devToolsPage.summonSearchBox(); |
| const inputElement = await devToolsPage.waitFor(SEARCH_QUERY); |
| assert.isOk(inputElement, 'Unable to find search input field'); |
| |
| await inputElement.focus(); |
| await inputElement.click({clickCount: 3}); // Select all text. |
| await inputElement.type(query); |
| |
| await devToolsPage.waitForFunction(async () => { |
| const searchStatusText = await devToolsPage.getTextContent('.search-results-matches'); |
| if (totalMatches > 0) { |
| return searchStatusText.endsWith(`of ${totalMatches}`); |
| } |
| return !searchStatusText; |
| }); |
| } |
| |
| async function setupPreviewTest( |
| devToolsPage: DevToolsPage, inspectedPage: InspectedPage, url: string, resourceName?: string) { |
| await navigateToNetworkTab('hello.html', devToolsPage, inspectedPage); |
| await inspectedPage.evaluate(url => fetch(url), url); |
| await waitForSomeRequestsToAppear(3, devToolsPage); |
| |
| if (!resourceName) { |
| resourceName = url.length > 20 ? url.substring(0, 19) + '…' : url; |
| } |
| |
| await selectRequestByName(resourceName, {devToolsPage}); |
| |
| const networkView = await devToolsPage.waitFor('.network-item-view'); |
| await devToolsPage.click('[aria-label=Preview].tabbed-pane-header-tab', { |
| root: networkView, |
| }); |
| await devToolsPage.waitFor('[aria-label=Preview].tabbed-pane-header-tab[aria-selected=true]', networkView); |
| // Wait for the preview to load. |
| await devToolsPage.click( |
| '.json-view, .shadow-xml-view, devtools-text-editor, .html-preview-frame', |
| {clickOptions: {offset: {x: 10, y: 10}}}); |
| } |
| |
| it('can be searched for JSON content', async ({devToolsPage, inspectedPage}) => { |
| const url = 'data:application/json,%5B533%2C3223%5D'; |
| await setupPreviewTest(devToolsPage, inspectedPage, url); |
| |
| await searchInPreview(devToolsPage, '533', 1); |
| await searchInPreview(devToolsPage, '322', 1); |
| }); |
| |
| it('can be searched for JSON content with special mime type', async ({devToolsPage, inspectedPage}) => { |
| const url = 'data:application/vnd.document+json,%7B%22foo0foo%22%3A%20123%7D'; |
| await setupPreviewTest(devToolsPage, inspectedPage, url); |
| await searchInPreview(devToolsPage, 'foo', 1); |
| }); |
| |
| it('can be searched for XML content', async ({devToolsPage, inspectedPage}) => { |
| const url = 'data:text/xml,%3Cbar%3E%3Cfoo%2F%3Etest%3C%2Fbar%3E'; |
| await setupPreviewTest(devToolsPage, inspectedPage, url); |
| await searchInPreview(devToolsPage, 'bar', 2); |
| await searchInPreview(devToolsPage, 'foo', 1); |
| await searchInPreview(devToolsPage, 'test', 1); |
| }); |
| |
| it('can be searched for XML content with comments', async ({devToolsPage, inspectedPage}) => { |
| const url = 'data:text/xml,%3Cbar%3E%3C!--%20FOO%20--%3E%3C%2Fbar%3E'; |
| await setupPreviewTest(devToolsPage, inspectedPage, url); |
| await searchInPreview(devToolsPage, 'FOO', 1); |
| await searchInPreview(devToolsPage, 'bar', 2); |
| }); |
| |
| it('can be searched for XML content with CDATA', async ({devToolsPage, inspectedPage}) => { |
| const url = 'data:text/xml,%3Ca%3E%3C!%5BCDATA%5BGGG%5D%5D%3E%3Cg%20tee%3D%22gee%22%3Etee%3C%2Fg%3E%3C%2Fa%3E'; |
| await setupPreviewTest(devToolsPage, inspectedPage, url); |
| await searchInPreview(devToolsPage, 'GGG', 1); |
| await searchInPreview(devToolsPage, 'tee', 2); |
| await searchInPreview(devToolsPage, 'CDATA', 1); |
| }); |
| |
| it('can be searched for JSON content with XML mime type', async ({devToolsPage, inspectedPage}) => { |
| const url = 'data:text/xml,%7B%22foo0%22%3A%20%22barr%22%2C%20%22barr%22%3A%20%22fooo%22%7D'; |
| await setupPreviewTest(devToolsPage, inspectedPage, url); |
| await searchInPreview(devToolsPage, 'fooo', 1); |
| await searchInPreview(devToolsPage, 'bar', 2); |
| }); |
| }); |
| }); |