diff --git a/src/renderer/CameraController.ts b/src/renderer/CameraController.ts index 7068b92b..18f1a03e 100644 --- a/src/renderer/CameraController.ts +++ b/src/renderer/CameraController.ts @@ -274,12 +274,12 @@ export class CameraController { // Vertical bounds if (this.#isFixedHeight && this.isScrollable()) { - // Fixed height mode with scrollable content + // Fixed height mode with scrollable content - allow vertical panning within bounds const maxY = 0; // Top of content at top of viewport const minY = this.#viewportHeight - this.#contentHeight; // Bottom of content at bottom of viewport boundedY = Math.max(minY, Math.min(maxY, desiredY)) - } else if (!this.#isFixedHeight) { - // Auto-height mode: no vertical panning + } else { + // Auto-height mode OR fixed height but not scrollable - no vertical panning boundedY = 0 } diff --git a/tests/edge-cases.spec.ts b/tests/edge-cases.spec.ts index fbd1f1c5..3b6b45f5 100644 --- a/tests/edge-cases.spec.ts +++ b/tests/edge-cases.spec.ts @@ -8,6 +8,72 @@ test.describe('FlameGraph Edge Cases Tests', () => { utils = new FlameGraphTestUtils(page) }) + test('top frames remain visible after dragging in fixed height mode', async ({ page }) => { + // This tests the fix for the bug where camera.y could become negative + // when in fixed height mode but content fits in viewport (not scrollable), + // causing top flamegraph bars to disappear. + await utils.navigateToTest() + + const canvas = await utils.getCanvasElement() + const box = await canvas.boundingBox() + expect(box).not.toBeNull() + + // Take a screenshot before dragging to capture the initial state + await expect(page).toHaveScreenshot('before-drag-fixed-height.png') + + // Perform a drag operation that would push content up (drag downward) + // In the buggy version, this would set camera.y to a negative value + const startX = box!.x + box!.width / 2 + const startY = box!.y + box!.height / 2 + const endY = box!.y + box!.height - 50 // Drag down significantly + + await page.mouse.move(startX, startY) + await page.mouse.down() + await page.mouse.move(startX, endY, { steps: 10 }) + await page.mouse.up() + + await page.waitForTimeout(500) + + // Take a screenshot after dragging - top frames should still be visible + // The fix ensures camera.y stays at 0 when content fits in viewport + await expect(page).toHaveScreenshot('after-drag-fixed-height.png') + + // Verify the canvas is still fully visible and functional + await expect(canvas).toBeVisible() + + // Click on the root frame area to verify it's still interactive + await utils.clickFrame(COMMON_POSITIONS.ROOT_FRAME.x, COMMON_POSITIONS.ROOT_FRAME.y) + await page.waitForTimeout(500) + + // The flamegraph should still be functional + await expect(canvas).toBeVisible() + }) + + test('top frames remain visible after fullscreen resize', async ({ page }) => { + // Test that going fullscreen doesn't cause top frames to disappear + await utils.navigateToTest() + + const canvas = await utils.getCanvasElement() + + // Initial state + await expect(canvas).toBeVisible() + await expect(page).toHaveScreenshot('before-fullscreen.png') + + // Simulate fullscreen by changing to a larger viewport + await page.setViewportSize({ width: 1920, height: 1080 }) + await page.waitForTimeout(1000) + + // Top frames should still be visible at the top of the canvas + await expect(canvas).toBeVisible() + await expect(page).toHaveScreenshot('after-fullscreen.png') + + // Verify the root frame is still clickable at the top + await utils.clickFrame(COMMON_POSITIONS.ROOT_FRAME.x, COMMON_POSITIONS.ROOT_FRAME.y) + await page.waitForTimeout(500) + + await expect(canvas).toBeVisible() + }) + test('handles extreme zoom levels', async ({ page }) => { await utils.navigateToTest() diff --git a/tests/snapshots/edge-cases.spec.ts/chromium-after-drag-fixed-height-.png b/tests/snapshots/edge-cases.spec.ts/chromium-after-drag-fixed-height-.png new file mode 100644 index 00000000..5465f08a Binary files /dev/null and b/tests/snapshots/edge-cases.spec.ts/chromium-after-drag-fixed-height-.png differ diff --git a/tests/snapshots/edge-cases.spec.ts/chromium-after-fullscreen-.png b/tests/snapshots/edge-cases.spec.ts/chromium-after-fullscreen-.png new file mode 100644 index 00000000..d026e943 Binary files /dev/null and b/tests/snapshots/edge-cases.spec.ts/chromium-after-fullscreen-.png differ diff --git a/tests/snapshots/edge-cases.spec.ts/chromium-before-drag-fixed-height-.png b/tests/snapshots/edge-cases.spec.ts/chromium-before-drag-fixed-height-.png new file mode 100644 index 00000000..46874660 Binary files /dev/null and b/tests/snapshots/edge-cases.spec.ts/chromium-before-drag-fixed-height-.png differ diff --git a/tests/snapshots/edge-cases.spec.ts/chromium-before-fullscreen-.png b/tests/snapshots/edge-cases.spec.ts/chromium-before-fullscreen-.png new file mode 100644 index 00000000..46874660 Binary files /dev/null and b/tests/snapshots/edge-cases.spec.ts/chromium-before-fullscreen-.png differ