Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions packages/trace-viewer/src/ui/networkFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@

import './networkFilters.css';

const resourceTypes = ['All', 'Fetch', 'HTML', 'JS', 'CSS', 'Font', 'Image'] as const;
const resourceTypes = ['Fetch', 'HTML', 'JS', 'CSS', 'Font', 'Image'] as const;
export type ResourceType = typeof resourceTypes[number];

export type FilterState = {
searchValue: string;
resourceType: ResourceType;
resourceTypes: Set<ResourceType>;
};

export const defaultFilterState: FilterState = { searchValue: '', resourceType: 'All' };
export const defaultFilterState: FilterState = { searchValue: '', resourceTypes: new Set() };

export const NetworkFilters = ({ filterState, onFilterStateChange }: {
filterState: FilterState,
Expand All @@ -41,12 +41,28 @@ export const NetworkFilters = ({ filterState, onFilterStateChange }: {
/>

<div className='network-filters-resource-types'>
<div
title='All'
onClick={() => onFilterStateChange({ ...filterState, resourceTypes: new Set() })}
className={`network-filters-resource-type ${filterState.resourceTypes.size === 0 ? 'selected' : ''}`}
>
All
</div>

{resourceTypes.map(resourceType => (
<div
key={resourceType}
title={resourceType}
onClick={() => onFilterStateChange({ ...filterState, resourceType })}
className={`network-filters-resource-type ${filterState.resourceType === resourceType ? 'selected' : ''}`}
onClick={event => {
let newType;
if (event.ctrlKey || event.metaKey)
newType = filterState.resourceTypes.symmetricDifference(new Set([resourceType]));
else
newType = new Set([resourceType]);

onFilterStateChange({ ...filterState, resourceTypes: newType });
}}
className={`network-filters-resource-type ${filterState.resourceTypes.has(resourceType) ? 'selected' : ''}`}
>
{resourceType}
</div>
Expand Down
8 changes: 3 additions & 5 deletions packages/trace-viewer/src/ui/networkTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,6 @@ function comparator(sortBy: ColumnName) {
}

const resourceTypePredicates: Record<ResourceType, (contentType: string) => boolean> = {
'All': () => true,
'Fetch': contentType => contentType === 'application/json',
'HTML': contentType => contentType === 'text/html',
'CSS': contentType => contentType === 'text/css',
Expand All @@ -379,10 +378,9 @@ const resourceTypePredicates: Record<ResourceType, (contentType: string) => bool
'Image': contentType => contentType.includes('image'),
};

function filterEntry({ searchValue, resourceType }: FilterState) {
function filterEntry({ searchValue, resourceTypes }: FilterState) {
return (entry: RenderedEntry) => {
const typePredicate = resourceTypePredicates[resourceType];

return typePredicate(entry.contentType) && entry.name.url.toLowerCase().includes(searchValue.toLowerCase());
const isRightType = resourceTypes.size === 0 || Array.from(resourceTypes).some(type => resourceTypePredicates[type](entry.contentType));
return isRightType && entry.name.url.toLowerCase().includes(searchValue.toLowerCase());
};
}
33 changes: 33 additions & 0 deletions tests/library/trace-viewer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,39 @@ test('should filter network requests by resource type', async ({ page, runAndTra
await expect(traceViewer.networkRequests.getByText('font.woff2')).toBeVisible();
});

test('should filter network requests by multiple resource types', async ({ page, runAndTrace, server }) => {
const traceViewer = await runAndTrace(async () => {
server.setRoute('/api/endpoint', (_, res) => res.setHeader('Content-Type', 'application/json').end());
await page.goto(`${server.PREFIX}/network-tab/network.html`);
await page.evaluate(() => (window as any).donePromise);
});
await traceViewer.selectAction('Navigate');
await traceViewer.showNetworkTab();

const { networkRequests } = traceViewer;

await traceViewer.page.getByText('JS', { exact: true }).click();
await expect(networkRequests).toHaveCount(1);
await expect(networkRequests.getByText('script.js')).toBeVisible();

await traceViewer.page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
await expect(networkRequests.getByText('script.js')).toBeVisible();
await expect(networkRequests.getByText('style.css')).toBeVisible();
await expect(networkRequests).toHaveCount(2);

await traceViewer.page.getByText('Image', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
await expect(networkRequests.getByText('image.png')).toBeVisible();
await expect(networkRequests).toHaveCount(3);

await traceViewer.page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
await expect(networkRequests).toHaveCount(2);
await expect(networkRequests.getByText('script.js')).toBeVisible();
await expect(networkRequests.getByText('image.png')).toBeVisible();

await traceViewer.page.getByText('All', { exact: true }).click();
await expect(networkRequests).toHaveCount(9);
});

test('should show font preview', async ({ page, runAndTrace, server }) => {
const traceViewer = await runAndTrace(async () => {
await page.goto(`${server.PREFIX}/network-tab/network.html`);
Expand Down
41 changes: 41 additions & 0 deletions tests/playwright-test/ui-mode-test-network-tab.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,47 @@ test('should filter network requests by resource type', async ({ runUITest, serv
await expect(networkItems.getByText('font.woff2')).toBeVisible();
});

test('should filter network requests by multiple resource types', async ({ runUITest, server }) => {
server.setRoute('/api/endpoint', (_, res) => res.setHeader('Content-Type', 'application/json').end());

const { page } = await runUITest({
'network-tab.test.ts': `
import { test, expect } from '@playwright/test';
test('network tab test', async ({ page }) => {
await page.goto('${server.PREFIX}/network-tab/network.html');
await page.evaluate(() => (window as any).donePromise);
});
`,
});

await page.getByText('network tab test').dblclick();
await page.getByText('Network', { exact: true }).click();

const networkItems = page.getByRole('list', { name: 'Network requests' }).getByRole('listitem');
await expect(networkItems).toHaveCount(9);

await page.getByText('JS', { exact: true }).click();
await expect(networkItems).toHaveCount(1);
await expect(networkItems.getByText('script.js')).toBeVisible();

await page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
await expect(networkItems.getByText('script.js')).toBeVisible();
await expect(networkItems.getByText('style.css')).toBeVisible();
await expect(networkItems).toHaveCount(2);

await page.getByText('Image', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
await expect(networkItems.getByText('image.png')).toBeVisible();
await expect(networkItems).toHaveCount(3);

await page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
await expect(networkItems).toHaveCount(2);
await expect(networkItems.getByText('script.js')).toBeVisible();
await expect(networkItems.getByText('image.png')).toBeVisible();

await page.getByText('All', { exact: true }).click();
await expect(networkItems).toHaveCount(9);
});

test('should filter network requests by url', async ({ runUITest, server }) => {
const { page } = await runUITest({
'network-tab.test.ts': `
Expand Down
Loading