Skip to content

Commit fbdb757

Browse files
authored
feat(trace): filter network on multiple types (#37910)
1 parent b47ae6c commit fbdb757

File tree

4 files changed

+98
-10
lines changed

4 files changed

+98
-10
lines changed

packages/trace-viewer/src/ui/networkFilters.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616

1717
import './networkFilters.css';
1818

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

2222
export type FilterState = {
2323
searchValue: string;
24-
resourceType: ResourceType;
24+
resourceTypes: Set<ResourceType>;
2525
};
2626

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

2929
export const NetworkFilters = ({ filterState, onFilterStateChange }: {
3030
filterState: FilterState,
@@ -41,12 +41,28 @@ export const NetworkFilters = ({ filterState, onFilterStateChange }: {
4141
/>
4242

4343
<div className='network-filters-resource-types'>
44+
<div
45+
title='All'
46+
onClick={() => onFilterStateChange({ ...filterState, resourceTypes: new Set() })}
47+
className={`network-filters-resource-type ${filterState.resourceTypes.size === 0 ? 'selected' : ''}`}
48+
>
49+
All
50+
</div>
51+
4452
{resourceTypes.map(resourceType => (
4553
<div
4654
key={resourceType}
4755
title={resourceType}
48-
onClick={() => onFilterStateChange({ ...filterState, resourceType })}
49-
className={`network-filters-resource-type ${filterState.resourceType === resourceType ? 'selected' : ''}`}
56+
onClick={event => {
57+
let newType;
58+
if (event.ctrlKey || event.metaKey)
59+
newType = filterState.resourceTypes.symmetricDifference(new Set([resourceType]));
60+
else
61+
newType = new Set([resourceType]);
62+
63+
onFilterStateChange({ ...filterState, resourceTypes: newType });
64+
}}
65+
className={`network-filters-resource-type ${filterState.resourceTypes.has(resourceType) ? 'selected' : ''}`}
5066
>
5167
{resourceType}
5268
</div>

packages/trace-viewer/src/ui/networkTab.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@ function comparator(sortBy: ColumnName) {
370370
}
371371

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

382-
function filterEntry({ searchValue, resourceType }: FilterState) {
381+
function filterEntry({ searchValue, resourceTypes }: FilterState) {
383382
return (entry: RenderedEntry) => {
384-
const typePredicate = resourceTypePredicates[resourceType];
385-
386-
return typePredicate(entry.contentType) && entry.name.url.toLowerCase().includes(searchValue.toLowerCase());
383+
const isRightType = resourceTypes.size === 0 || Array.from(resourceTypes).some(type => resourceTypePredicates[type](entry.contentType));
384+
return isRightType && entry.name.url.toLowerCase().includes(searchValue.toLowerCase());
387385
};
388386
}

tests/library/trace-viewer.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,39 @@ test('should filter network requests by resource type', async ({ page, runAndTra
406406
await expect(traceViewer.networkRequests.getByText('font.woff2')).toBeVisible();
407407
});
408408

409+
test('should filter network requests by multiple resource types', async ({ page, runAndTrace, server }) => {
410+
const traceViewer = await runAndTrace(async () => {
411+
server.setRoute('/api/endpoint', (_, res) => res.setHeader('Content-Type', 'application/json').end());
412+
await page.goto(`${server.PREFIX}/network-tab/network.html`);
413+
await page.evaluate(() => (window as any).donePromise);
414+
});
415+
await traceViewer.selectAction('Navigate');
416+
await traceViewer.showNetworkTab();
417+
418+
const { networkRequests } = traceViewer;
419+
420+
await traceViewer.page.getByText('JS', { exact: true }).click();
421+
await expect(networkRequests).toHaveCount(1);
422+
await expect(networkRequests.getByText('script.js')).toBeVisible();
423+
424+
await traceViewer.page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
425+
await expect(networkRequests.getByText('script.js')).toBeVisible();
426+
await expect(networkRequests.getByText('style.css')).toBeVisible();
427+
await expect(networkRequests).toHaveCount(2);
428+
429+
await traceViewer.page.getByText('Image', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
430+
await expect(networkRequests.getByText('image.png')).toBeVisible();
431+
await expect(networkRequests).toHaveCount(3);
432+
433+
await traceViewer.page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
434+
await expect(networkRequests).toHaveCount(2);
435+
await expect(networkRequests.getByText('script.js')).toBeVisible();
436+
await expect(networkRequests.getByText('image.png')).toBeVisible();
437+
438+
await traceViewer.page.getByText('All', { exact: true }).click();
439+
await expect(networkRequests).toHaveCount(9);
440+
});
441+
409442
test('should show font preview', async ({ page, runAndTrace, server }) => {
410443
const traceViewer = await runAndTrace(async () => {
411444
await page.goto(`${server.PREFIX}/network-tab/network.html`);

tests/playwright-test/ui-mode-test-network-tab.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,47 @@ test('should filter network requests by resource type', async ({ runUITest, serv
5959
await expect(networkItems.getByText('font.woff2')).toBeVisible();
6060
});
6161

62+
test('should filter network requests by multiple resource types', async ({ runUITest, server }) => {
63+
server.setRoute('/api/endpoint', (_, res) => res.setHeader('Content-Type', 'application/json').end());
64+
65+
const { page } = await runUITest({
66+
'network-tab.test.ts': `
67+
import { test, expect } from '@playwright/test';
68+
test('network tab test', async ({ page }) => {
69+
await page.goto('${server.PREFIX}/network-tab/network.html');
70+
await page.evaluate(() => (window as any).donePromise);
71+
});
72+
`,
73+
});
74+
75+
await page.getByText('network tab test').dblclick();
76+
await page.getByText('Network', { exact: true }).click();
77+
78+
const networkItems = page.getByRole('list', { name: 'Network requests' }).getByRole('listitem');
79+
await expect(networkItems).toHaveCount(9);
80+
81+
await page.getByText('JS', { exact: true }).click();
82+
await expect(networkItems).toHaveCount(1);
83+
await expect(networkItems.getByText('script.js')).toBeVisible();
84+
85+
await page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
86+
await expect(networkItems.getByText('script.js')).toBeVisible();
87+
await expect(networkItems.getByText('style.css')).toBeVisible();
88+
await expect(networkItems).toHaveCount(2);
89+
90+
await page.getByText('Image', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
91+
await expect(networkItems.getByText('image.png')).toBeVisible();
92+
await expect(networkItems).toHaveCount(3);
93+
94+
await page.getByText('CSS', { exact: true }).click({ modifiers: ['ControlOrMeta'] });
95+
await expect(networkItems).toHaveCount(2);
96+
await expect(networkItems.getByText('script.js')).toBeVisible();
97+
await expect(networkItems.getByText('image.png')).toBeVisible();
98+
99+
await page.getByText('All', { exact: true }).click();
100+
await expect(networkItems).toHaveCount(9);
101+
});
102+
62103
test('should filter network requests by url', async ({ runUITest, server }) => {
63104
const { page } = await runUITest({
64105
'network-tab.test.ts': `

0 commit comments

Comments
 (0)