fix(devtools): network tab panel inner scroll (#8329)

* fix: ui overflow for the network tab

* test(e2e): scroller behaviour for network tab
This commit is contained in:
Sid
2026-06-22 19:32:29 +05:30
committed by GitHub
parent 46498e8423
commit 222ac80f5e
6 changed files with 237 additions and 0 deletions

View File

@@ -314,6 +314,7 @@ const StyledWrapper = styled.div`
height: 100% !important;
max-height: 400px !important;
padding: 0.5rem !important;
overflow: auto !important;
.network-logs-pre {
color: ${(props) => props.theme.console.messageColor} !important;

View File

@@ -0,0 +1,9 @@
{
"version": "1",
"name": "network-log-scroll",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}

View File

@@ -0,0 +1,104 @@
meta {
name: network-log-scroll
type: http
seq: 1
}
get {
url: http://localhost:8081/api/echo/headers
body: none
auth: none
}
headers {
X-Network-Log-1: value-1
X-Network-Log-2: value-2
X-Network-Log-3: value-3
X-Network-Log-4: value-4
X-Network-Log-5: value-5
X-Network-Log-6: value-6
X-Network-Log-7: value-7
X-Network-Log-8: value-8
X-Network-Log-9: value-9
X-Network-Log-10: value-10
X-Network-Log-11: value-11
X-Network-Log-12: value-12
X-Network-Log-13: value-13
X-Network-Log-14: value-14
X-Network-Log-15: value-15
X-Network-Log-16: value-16
X-Network-Log-17: value-17
X-Network-Log-18: value-18
X-Network-Log-19: value-19
X-Network-Log-20: value-20
X-Network-Log-21: value-21
X-Network-Log-22: value-22
X-Network-Log-23: value-23
X-Network-Log-24: value-24
X-Network-Log-25: value-25
X-Network-Log-26: value-26
X-Network-Log-27: value-27
X-Network-Log-28: value-28
X-Network-Log-29: value-29
X-Network-Log-30: value-30
X-Network-Log-31: value-31
X-Network-Log-32: value-32
X-Network-Log-33: value-33
X-Network-Log-34: value-34
X-Network-Log-35: value-35
X-Network-Log-36: value-36
X-Network-Log-37: value-37
X-Network-Log-38: value-38
X-Network-Log-39: value-39
X-Network-Log-40: value-40
X-Network-Log-41: value-41
X-Network-Log-42: value-42
X-Network-Log-43: value-43
X-Network-Log-44: value-44
X-Network-Log-45: value-45
X-Network-Log-46: value-46
X-Network-Log-47: value-47
X-Network-Log-48: value-48
X-Network-Log-49: value-49
X-Network-Log-50: value-50
X-Network-Log-51: value-51
X-Network-Log-52: value-52
X-Network-Log-53: value-53
X-Network-Log-54: value-54
X-Network-Log-55: value-55
X-Network-Log-56: value-56
X-Network-Log-57: value-57
X-Network-Log-58: value-58
X-Network-Log-59: value-59
X-Network-Log-60: value-60
X-Network-Log-61: value-61
X-Network-Log-62: value-62
X-Network-Log-63: value-63
X-Network-Log-64: value-64
X-Network-Log-65: value-65
X-Network-Log-66: value-66
X-Network-Log-67: value-67
X-Network-Log-68: value-68
X-Network-Log-69: value-69
X-Network-Log-70: value-70
X-Network-Log-71: value-71
X-Network-Log-72: value-72
X-Network-Log-73: value-73
X-Network-Log-74: value-74
X-Network-Log-75: value-75
X-Network-Log-76: value-76
X-Network-Log-77: value-77
X-Network-Log-78: value-78
X-Network-Log-79: value-79
X-Network-Log-80: value-80
X-Network-Log-81: value-81
X-Network-Log-82: value-82
X-Network-Log-83: value-83
X-Network-Log-84: value-84
X-Network-Log-85: value-85
X-Network-Log-86: value-86
X-Network-Log-87: value-87
X-Network-Log-88: value-88
X-Network-Log-89: value-89
X-Network-Log-90: value-90
}

View File

@@ -0,0 +1,10 @@
{
"collections": [
{
"path": "{{collectionPath}}",
"securityConfig": {
"jsSandboxMode": "safe"
}
}
]
}

View File

@@ -0,0 +1,12 @@
{
"maximized": false,
"lastOpenedCollections": [
"{{collectionPath}}"
],
"preferences": {
"onboarding": {
"hasLaunchedBefore": true,
"hasSeenWelcomeModal": true
}
}
}

View File

@@ -0,0 +1,101 @@
import { test, expect, type Locator } from '../../../playwright';
import { openCollection, openRequest, sendRequest } from '../../utils/page';
const COLLECTION_NAME = 'network-log-scroll';
const REQUEST_NAME = 'network-log-scroll';
const isEntryVisibleInScroller = async (scroller: Locator, entry: Locator) => {
const entryHandle = await entry.elementHandle();
if (!entryHandle) {
return false;
}
return scroller.evaluate((container, entryEl) => {
const containerRect = container.getBoundingClientRect();
const entryRect = entryEl.getBoundingClientRect();
return (
entryRect.height > 0
&& entryRect.top >= containerRect.top - 1
&& entryRect.bottom <= containerRect.bottom + 1
);
}, entryHandle);
};
test.describe('DevTools Network Log Details Scroll', () => {
test('last network log lines are visible and scrollable in request details panel', async ({ pageWithUserData: page }) => {
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
await test.step('Open fixture request and send it', async () => {
await openCollection(page, COLLECTION_NAME);
await openRequest(page, COLLECTION_NAME, REQUEST_NAME);
await sendRequest(page, 200);
});
await test.step('Open DevTools Network tab and select the request', async () => {
await page.locator('button[data-trigger="dev-tools"]').click();
await expect(page.locator('.console-header')).toBeVisible();
const networkTab = page.locator('.console-tab').filter({ hasText: 'Network' });
await expect(networkTab).toBeVisible();
await networkTab.click();
await expect(networkTab).toHaveClass(/active/);
const requestRow = page.getByTestId('network-request-row').first();
await expect(requestRow).toBeVisible();
await requestRow.click();
});
const panel = page.locator('.details-panel-wrapper');
const outerScroller = panel.locator('.panel-content');
const innerScroller = panel.locator('.network-logs-wrapper .network-logs-container');
const lastEntry = innerScroller.locator('.network-logs-entry').last();
await test.step('Open Network sub-tab in request details panel', async () => {
await expect(panel.getByText('Request Details')).toBeVisible();
const networkSubTab = panel.locator('.tab-button').filter({ hasText: 'Network' });
await expect(networkSubTab).toBeVisible();
await networkSubTab.click();
await expect(networkSubTab).toHaveClass(/active/);
await expect(innerScroller).toBeVisible();
});
await test.step('Verify nested inner scroller overflows', async () => {
const innerOverflows = await innerScroller.evaluate((el) => el.scrollHeight > el.clientHeight);
expect(innerOverflows).toBe(true);
await expect(lastEntry).toContainText(/Request completed in/);
});
await test.step('Outer panel scroll alone does not reveal the last log line', async () => {
const initialOuterScrollTop = await outerScroller.evaluate((el) => el.scrollTop);
expect(initialOuterScrollTop).toBe(0);
await outerScroller.evaluate((el) => {
el.scrollTop = el.scrollHeight;
});
await expect.poll(() => isEntryVisibleInScroller(innerScroller, lastEntry)).toBe(false);
await outerScroller.evaluate((el) => {
el.scrollTop = 0;
});
});
await test.step('Scroll nested inner container and verify last log line is visible in viewport', async () => {
await expect(async () => {
await innerScroller.evaluate((el) => {
el.scrollTop = el.scrollHeight;
});
const scrollTop = await innerScroller.evaluate((el) => el.scrollTop);
expect(scrollTop).toBeGreaterThan(0);
await expect(lastEntry).toBeVisible({ timeout: 1000 });
expect(await isEntryVisibleInScroller(innerScroller, lastEntry)).toBe(true);
}).toPass({ timeout: 10000 });
const outerScrollTop = await outerScroller.evaluate((el) => el.scrollTop);
expect(outerScrollTop).toBe(0);
});
});
});