diff --git a/tests/mjs/29-background-patterns.test.mjs b/tests/mjs/29-background-patterns.test.mjs new file mode 100644 index 0000000..1782cff --- /dev/null +++ b/tests/mjs/29-background-patterns.test.mjs @@ -0,0 +1,216 @@ +/** + * Background Pattern Tests + * + * Validates that background patterns are correctly applied in both light and dark themes. + * Tests cover: + * - Pattern existence (not "none") + * - Correct background colors + * - Pattern application to body element + * - Theme switching maintains patterns + */ + +import { test, expect } from '@playwright/test'; + +const URL = process.env.TEST_URL || 'http://localhost:1999'; +const CURRENT_YEAR = new Date().getFullYear(); + +test.describe('Background Patterns', () => { + + test('Light theme has concentric squares pattern on light gray background', async ({ page }) => { + // Navigate to light theme + await page.goto(`${URL}/?lang=en`); + await page.evaluate(() => { + localStorage.setItem('color-theme-mode', 'light'); + document.documentElement.setAttribute('data-color-theme', 'light'); + }); + await page.reload(); + await page.waitForTimeout(500); + + // Get computed styles + const styles = await page.evaluate(() => { + const body = document.body; + const computed = window.getComputedStyle(body); + return { + backgroundColor: computed.backgroundColor, + backgroundImage: computed.backgroundImage, + backgroundSize: computed.backgroundSize, + backgroundAttachment: computed.backgroundAttachment, + themeAttr: document.documentElement.getAttribute('data-color-theme') + }; + }); + + // Verify background color is light gray (#d6d6d6) + expect(styles.backgroundColor).toBe('rgb(214, 214, 214)'); + + // Verify pattern exists (not "none") + expect(styles.backgroundImage).not.toBe('none'); + + // Verify it's a concentric squares pattern (contains repeating-linear-gradient) + expect(styles.backgroundImage).toContain('repeating-linear-gradient'); + + // Verify background size is set (may have multiple values for multiple gradient layers) + expect(styles.backgroundSize).toContain('40px 40px'); + + // Verify background attachment is fixed (may have multiple values for multiple gradient layers) + expect(styles.backgroundAttachment).toContain('fixed'); + + console.log('✅ Light theme: Light gray background (#d6d6d6) with concentric squares pattern'); + }); + + test('Dark theme has diagonal green grid pattern on medium gray background', async ({ page }) => { + // Navigate to dark theme + await page.goto(`${URL}/?lang=en`); + await page.evaluate(() => { + localStorage.setItem('color-theme-mode', 'dark'); + document.documentElement.setAttribute('data-color-theme', 'dark'); + }); + await page.reload(); + await page.waitForTimeout(500); + + // Get computed styles + const styles = await page.evaluate(() => { + const body = document.body; + const computed = window.getComputedStyle(body); + return { + backgroundColor: computed.backgroundColor, + backgroundImage: computed.backgroundImage, + backgroundSize: computed.backgroundSize, + backgroundAttachment: computed.backgroundAttachment, + themeAttr: document.documentElement.getAttribute('data-color-theme') + }; + }); + + // Verify background color is medium gray (#3a3a3a) + expect(styles.backgroundColor).toBe('rgb(58, 58, 58)'); + + // Verify pattern exists (not "none") + expect(styles.backgroundImage).not.toBe('none'); + + // Verify it's a diagonal grid pattern (contains 45deg and green color) + expect(styles.backgroundImage).toContain('45deg'); + expect(styles.backgroundImage).toContain('rgba(0, 255, 128'); + + // Verify background size is set (may have multiple values for multiple gradient layers) + expect(styles.backgroundSize).toContain('40px 40px'); + + // Verify background attachment is fixed (may have multiple values for multiple gradient layers) + expect(styles.backgroundAttachment).toContain('fixed'); + + console.log('✅ Dark theme: Medium gray background (#3a3a3a) with diagonal green grid pattern'); + }); + + test('Pattern persists when switching between themes', async ({ page }) => { + await page.goto(`${URL}/?lang=en`); + + // Start with light theme + await page.evaluate(() => { + localStorage.setItem('color-theme-mode', 'light'); + document.documentElement.setAttribute('data-color-theme', 'light'); + }); + await page.reload(); + await page.waitForTimeout(500); + + const lightPattern = await page.evaluate(() => { + return window.getComputedStyle(document.body).backgroundImage; + }); + + expect(lightPattern).not.toBe('none'); + expect(lightPattern).toContain('repeating-linear-gradient'); + + // Switch to dark theme + await page.evaluate(() => { + localStorage.setItem('color-theme-mode', 'dark'); + document.documentElement.setAttribute('data-color-theme', 'dark'); + }); + await page.reload(); + await page.waitForTimeout(500); + + const darkPattern = await page.evaluate(() => { + return window.getComputedStyle(document.body).backgroundImage; + }); + + expect(darkPattern).not.toBe('none'); + expect(darkPattern).toContain('45deg'); + expect(darkPattern).toContain('rgba(0, 255, 128'); + + // Patterns should be different + expect(lightPattern).not.toBe(darkPattern); + + console.log('✅ Patterns persist and change correctly when switching themes'); + }); + + test('Auto theme uses correct pattern based on system preference', async ({ page }) => { + // Test auto theme in light mode + await page.emulateMedia({ colorScheme: 'light' }); + await page.goto(`${URL}/?lang=en`); + await page.evaluate(() => { + localStorage.setItem('color-theme-mode', 'auto'); + document.documentElement.setAttribute('data-color-theme', 'auto'); + }); + await page.reload(); + await page.waitForTimeout(500); + + const autoLightStyles = await page.evaluate(() => { + const computed = window.getComputedStyle(document.body); + return { + backgroundColor: computed.backgroundColor, + backgroundImage: computed.backgroundImage + }; + }); + + // In light mode, auto theme should use light gray background with concentric squares pattern + expect(autoLightStyles.backgroundColor).toBe('rgb(214, 214, 214)'); + expect(autoLightStyles.backgroundImage).toContain('repeating-linear-gradient'); + + // Test auto theme in dark mode + await page.emulateMedia({ colorScheme: 'dark' }); + await page.reload(); + await page.waitForTimeout(500); + + const autoDarkStyles = await page.evaluate(() => { + const computed = window.getComputedStyle(document.body); + return { + backgroundColor: computed.backgroundColor, + backgroundImage: computed.backgroundImage + }; + }); + + // In dark mode, auto theme should use medium gray background with diagonal grid + expect(autoDarkStyles.backgroundColor).toBe('rgb(58, 58, 58)'); + expect(autoDarkStyles.backgroundImage).toContain('45deg'); + expect(autoDarkStyles.backgroundImage).toContain('rgba(0, 255, 128'); + + console.log('✅ Auto theme uses correct patterns based on system preference'); + }); + + test('Take visual screenshots of both patterns for manual verification', async ({ page }) => { + // Light mode screenshot + await page.goto(`${URL}/?lang=en`); + await page.evaluate(() => { + localStorage.setItem('color-theme-mode', 'light'); + document.documentElement.setAttribute('data-color-theme', 'light'); + }); + await page.reload(); + await page.waitForTimeout(500); + + await page.screenshot({ + path: 'tests/screenshots/pattern-light-theme.png', + fullPage: false + }); + + // Dark mode screenshot + await page.evaluate(() => { + localStorage.setItem('color-theme-mode', 'dark'); + document.documentElement.setAttribute('data-color-theme', 'dark'); + }); + await page.reload(); + await page.waitForTimeout(500); + + await page.screenshot({ + path: 'tests/screenshots/pattern-dark-theme.png', + fullPage: false + }); + + console.log('✅ Screenshots saved to tests/screenshots/'); + }); +});