chess/tests/ui/column-resize.test.js
Christoph Wagner fb96963b48
Some checks failed
CI Pipeline / Code Linting (pull_request) Successful in 14s
CI Pipeline / Run Tests (pull_request) Failing after 21s
CI Pipeline / Build Verification (pull_request) Has been skipped
CI Pipeline / Generate Quality Report (pull_request) Successful in 21s
fix: add status message element and fix column resizing bug
This commit fixes two bugs:

1. Issue #7: Missing status message DOM element
   - Added #status-message div to index.html
   - Added CSS styling with type-based classes (info, success, error)
   - Enhanced showMessage() to apply type classes for visual styling
   - Messages auto-hide after 3 seconds with fade-in animation

2. Column resizing visual bug:
   - Changed grid-template-columns from flexible (1fr 3fr 1fr)
   - To fixed minimum widths: minmax(200px, 250px) minmax(600px, 3fr) minmax(200px, 250px)
   - Prevents columns from resizing when content changes (captured pieces, move history)
   - Maintains stable layout throughout gameplay

Tests:
- Added status-message.test.js with 10 test cases
- Added column-resize.test.js with 8 test cases
- Tests verify DOM element existence, CSS styling, auto-hide behavior, and layout stability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 19:15:50 +01:00

213 lines
8.4 KiB
JavaScript

/**
* Column Resize Bug Tests
* Tests to verify that columns maintain consistent width during gameplay
*/
import { test, expect } from '@playwright/test';
test.describe('Column Layout Stability', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:8080');
await page.waitForLoadState('networkidle');
});
test('game container uses fixed minimum widths', async ({ page }) => {
const gameContainer = await page.locator('.game-container');
// Check computed styles
const styles = await gameContainer.evaluate((el) => {
const computed = window.getComputedStyle(el);
return {
display: computed.display,
gridTemplateColumns: computed.gridTemplateColumns
};
});
expect(styles.display).toBe('grid');
// Should use minmax() for fixed minimum widths
expect(styles.gridTemplateColumns).not.toBe('1fr 3fr 1fr');
});
test('left sidebar maintains minimum width', async ({ page }) => {
const leftSidebar = await page.locator('.captured-white');
const initialWidth = await leftSidebar.evaluate(el => el.offsetWidth);
expect(initialWidth).toBeGreaterThanOrEqual(200); // minmax(200px, 250px)
});
test('right sidebar maintains minimum width', async ({ page }) => {
const rightSidebar = await page.locator('.game-sidebar');
const initialWidth = await rightSidebar.evaluate(el => el.offsetWidth);
expect(initialWidth).toBeGreaterThanOrEqual(200); // minmax(200px, 250px)
});
test('board section maintains minimum width', async ({ page }) => {
const boardSection = await page.locator('.board-section');
const initialWidth = await boardSection.evaluate(el => el.offsetWidth);
expect(initialWidth).toBeGreaterThanOrEqual(600); // minmax(600px, 3fr)
});
test('columns do not resize when pieces are captured', async ({ page }) => {
// Get initial widths
const getWidths = async () => {
return await page.evaluate(() => {
const leftSidebar = document.querySelector('.captured-white');
const boardSection = document.querySelector('.board-section');
const rightSidebar = document.querySelector('.game-sidebar');
return {
left: leftSidebar.offsetWidth,
board: boardSection.offsetWidth,
right: rightSidebar.offsetWidth
};
});
};
const initialWidths = await getWidths();
// Make a move that captures a piece (simulate)
await page.evaluate(() => {
// Add captured piece to test resize behavior
const capturedList = document.querySelector('#captured-white-pieces');
if (capturedList) {
const piece = document.createElement('div');
piece.className = 'captured-piece white pawn';
piece.textContent = '♙';
capturedList.appendChild(piece);
}
});
await page.waitForTimeout(100); // Allow for any layout recalculation
const afterCaptureWidths = await getWidths();
// Columns should maintain their widths (within 1px for rounding)
expect(Math.abs(afterCaptureWidths.left - initialWidths.left)).toBeLessThanOrEqual(1);
expect(Math.abs(afterCaptureWidths.board - initialWidths.board)).toBeLessThanOrEqual(1);
expect(Math.abs(afterCaptureWidths.right - initialWidths.right)).toBeLessThanOrEqual(1);
});
test('columns do not resize when multiple pieces are captured', async ({ page }) => {
const getWidths = async () => {
return await page.evaluate(() => {
const leftSidebar = document.querySelector('.captured-white');
const boardSection = document.querySelector('.board-section');
const rightSidebar = document.querySelector('.game-sidebar');
return {
left: leftSidebar.offsetWidth,
board: boardSection.offsetWidth,
right: rightSidebar.offsetWidth
};
});
};
const initialWidths = await getWidths();
// Add multiple captured pieces
await page.evaluate(() => {
const capturedList = document.querySelector('#captured-white-pieces');
if (capturedList) {
const pieces = ['♙', '♘', '♗', '♖', '♕'];
pieces.forEach(symbol => {
const piece = document.createElement('div');
piece.className = 'captured-piece white';
piece.textContent = symbol;
capturedList.appendChild(piece);
});
}
});
await page.waitForTimeout(100);
const afterMultipleCapturesWidths = await getWidths();
// Columns should still maintain their widths
expect(Math.abs(afterMultipleCapturesWidths.left - initialWidths.left)).toBeLessThanOrEqual(1);
expect(Math.abs(afterMultipleCapturesWidths.board - initialWidths.board)).toBeLessThanOrEqual(1);
expect(Math.abs(afterMultipleCapturesWidths.right - initialWidths.right)).toBeLessThanOrEqual(1);
});
test('columns do not resize when move history grows', async ({ page }) => {
const getWidths = async () => {
return await page.evaluate(() => {
const leftSidebar = document.querySelector('.captured-white');
const boardSection = document.querySelector('.board-section');
const rightSidebar = document.querySelector('.game-sidebar');
return {
left: leftSidebar.offsetWidth,
board: boardSection.offsetWidth,
right: rightSidebar.offsetWidth
};
});
};
const initialWidths = await getWidths();
// Add move history entries
await page.evaluate(() => {
const moveHistory = document.querySelector('#move-history');
if (moveHistory) {
for (let i = 1; i <= 20; i++) {
const moveEntry = document.createElement('div');
moveEntry.className = 'move-entry';
moveEntry.innerHTML = `
<span class="move-number">${i}.</span>
<span class="move-notation white">e4</span>
<span class="move-notation black">e5</span>
`;
moveHistory.appendChild(moveEntry);
}
}
});
await page.waitForTimeout(100);
const afterMovesWidths = await getWidths();
// Columns should maintain their widths
expect(Math.abs(afterMovesWidths.left - initialWidths.left)).toBeLessThanOrEqual(1);
expect(Math.abs(afterMovesWidths.board - initialWidths.board)).toBeLessThanOrEqual(1);
expect(Math.abs(afterMovesWidths.right - initialWidths.right)).toBeLessThanOrEqual(1);
});
test('layout remains stable across window resize', async ({ page }) => {
// Set initial viewport
await page.setViewportSize({ width: 1400, height: 900 });
const getWidths = async () => {
return await page.evaluate(() => {
const leftSidebar = document.querySelector('.captured-white');
const boardSection = document.querySelector('.board-section');
const rightSidebar = document.querySelector('.game-sidebar');
return {
left: leftSidebar.offsetWidth,
board: boardSection.offsetWidth,
right: rightSidebar.offsetWidth
};
});
};
const widthsBefore = await getWidths();
// Resize window
await page.setViewportSize({ width: 1600, height: 900 });
await page.waitForTimeout(200);
const widthsAfter = await getWidths();
// Sidebar widths should remain close to their minimum (200px)
expect(widthsAfter.left).toBeGreaterThanOrEqual(200);
expect(widthsAfter.left).toBeLessThanOrEqual(250);
expect(widthsAfter.right).toBeGreaterThanOrEqual(200);
expect(widthsAfter.right).toBeLessThanOrEqual(250);
// Board should be able to grow
expect(widthsAfter.board).toBeGreaterThanOrEqual(600);
});
});