import type { ThemeExtractionResult } from '@/schema/theme-extraction'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createBoundaryValueTheme, createInvalidColorTheme, createLowContrastTheme, createPerformanceTestTheme, createValidTheme, } from './test-helpers'; import { ThemeCustomizer } from './theme-customizer'; /** * 高コントラストテーマデータを生成 */ function createHighContrastTheme(): ThemeExtractionResult { const theme = createValidTheme(); return { ...theme, colors: { ...theme.colors, primary_text_color: '#000000', primary_background_color: '#ffffff', secondary_text_color: '#000000', secondary_background_color: '#ffffff', inverse_text_color: '#ffffff', tertiary_background_color: '#000000', }, }; } function extractVariableValue(css: string, variableName: string): string | null { const regex = new RegExp(`${variableName}:\s*([^;]+)`); const match = css.match(regex); return match ? match[1].trim() : null; } describe('ThemeCustomizer', () => { describe('初期化・設定', () => { describe('コンストラクタオプション', () => { it('should initialize with default options when no options provided', () => { // Arrange // (no specific arrangement needed) // Act const customizer = new ThemeCustomizer(); const stats = customizer.getCacheStats(); // Assert expect(stats.maxSize).toBe(100); }); it('should override default options with provided options', () => { // Arrange const options = { enableCache: false, validateColors: false, checkAccessibility: false, performanceMode: true, }; // Act const customizer = new ThemeCustomizer(options); const theme = createValidTheme(); const result = customizer.generateThemedCSS(theme); // Assert expect(result.cacheHit).toBe(false); // キャッシュ無効なので常にfalse }); describe.each([ [true, true, true, false], [false, false, false, true], [true, false, true, false], [false, true, false, true], ])( 'option combinations: cache=%s, validate=%s, accessibility=%s, performance=%s', (enableCache, validateColors, checkAccessibility, performanceMode) => { it('should work with given option combination', () => { // Arrange const options = { enableCache, validateColors, checkAccessibility, performanceMode }; const customizer = new ThemeCustomizer(options); const theme = createValidTheme(); // Act const result = customizer.generateThemedCSS(theme); // Assert expect(result).toBeDefined(); expect(result.fullCSS).toBeDefined(); }); }, ); }); }); describe('CSS生成機能', () => { let customizer: ThemeCustomizer; beforeEach(() => { customizer = new ThemeCustomizer({ enableCache: true, validateColors: true, checkAccessibility: true, }); }); describe('正常系', () => { it('should generate complete CSS with valid theme', () => { // Arrange const validTheme = createValidTheme(); // Act const result = customizer.generateThemedCSS(validTheme); // Assert expect(result.fullCSS).toBeDefined(); expect(result.themeCSS).toBeDefined(); expect(result.baseCSS).toBeDefined(); expect(result.cacheHit).toBe(false); expect(result.generationTimeMs).toBeGreaterThan(0); }); it('should include extracted color variables in generated CSS', () => { // Arrange const validTheme = createValidTheme(); // Act const result = customizer.generateThemedCSS(validTheme); // Assert expect(extractVariableValue(result.themeCSS, '--extracted-primary-color')).toBe(validTheme.colors.primary_color); expect(extractVariableValue(result.themeCSS, '--extracted-secondary-color')).toBe(validTheme.colors.secondary_color); }); it('should include fallback values for theme variables', () => { // Arrange const validTheme = createValidTheme(); // Act const result = customizer.generateThemedCSS(validTheme); // Assert expect(result.themeCSS).toContain('var(--extracted-primary-color, #5a6c7d)'); expect(result.themeCSS).toContain('var(--extracted-primary-background-color, white)'); }); it('should include legacy color mapping variables', () => { // Arrange const validTheme = createValidTheme(); // Act const result = customizer.generateThemedCSS(validTheme); // Assert expect(result.themeCSS).toContain('--legacy-primary-text: var(--theme-primary-text)'); expect(result.themeCSS).toContain('--section-white-bg: var(--theme-primary-bg)'); }); }); describe('異常系', () => { it('should return fallback CSS when validation fails', () => { // Arrange const invalidTheme = createInvalidColorTheme(); const customizerWithValidation = new ThemeCustomizer({ validateColors: true }); // Act const result = customizerWithValidation.generateThemedCSS(invalidTheme); // Assert expect(result.fullCSS).toBeDefined(); expect(result.themeCSS).toBe(''); // テーマCSSは空 expect(result.baseCSS).toBeDefined(); // ベースCSSは提供される }); it('should generate CSS with invalid colors when validation disabled', () => { // Arrange const invalidTheme = createInvalidColorTheme(); const customizerNoValidation = new ThemeCustomizer({ validateColors: false }); // Act const result = customizerNoValidation.generateThemedCSS(invalidTheme); // Assert expect(result.fullCSS).toBeDefined(); expect(result.themeCSS).toContain('--extracted-primary-color: invalid-color'); }); }); describe('境界値・エッジケース', () => { it('should handle theme with empty color values', () => { // Arrange const themeWithEmptyColors = createValidTheme(); themeWithEmptyColors.colors.primary_color = ''; const customizerNoValidation = new ThemeCustomizer({ validateColors: false }); // Act const result = customizerNoValidation.generateThemedCSS(themeWithEmptyColors); // Assert expect(result.fullCSS).toBeDefined(); expect(result.themeCSS).toContain('--extracted-primary-color: '); }); it('should handle theme with all required color properties', () => { // Arrange const minimalTheme = createValidTheme(); // Act const result = customizer.generateThemedCSS(minimalTheme); // Assert expect(result.fullCSS).toBeDefined(); expect(Object.keys(minimalTheme.colors)).toHaveLength(15); // 15色全て確認 }); }); }); describe('バリデーション機能', () => { let validator: ThemeCustomizer; beforeEach(() => { validator = new ThemeCustomizer({ validateColors: true, checkAccessibility: true, }); }); describe('カラーコード検証', () => { it('should accept valid HEX color codes', () => { // Arrange const validTheme = createValidTheme(); // Act const validation = validator.validateTheme(validTheme); // Assert expect(validation.isValid).toBe(true); expect(validation.errors).toHaveLength(0); }); describe.each([ ['#000000', true], ['#FFFFFF', true], ['#5a6c7d', true], ['#F59E0B', true], ['#ggg', false], ['rgb(255,0,0)', false], ['#ff', false], ['#1234567', false], ['red', false], ['', false], ['invalid-color', false], ['#gggggg', false], ])('color validation: %s should be %s', (color, isValid) => { it(`should ${isValid ? 'accept' : 'reject'} color: ${color}`, () => { // Arrange const theme = createValidTheme(); theme.colors.primary_color = color; // Act const validation = validator.validateTheme(theme); // Assert expect(validation.isValid).toBe(isValid); if (!isValid) { expect(validation.errors.some((err) => err.includes('primary_color'))).toBe(true); } }); }); it('should detect multiple invalid color codes', () => { // Arrange const invalidTheme = createInvalidColorTheme(); // Act const validation = validator.validateTheme(invalidTheme); // Assert expect(validation.isValid).toBe(false); expect(validation.errors).toContain('primary_color: 無効なカラーコード形式 (invalid-color)'); expect(validation.errors).toContain('secondary_color: 無効なカラーコード形式 (#gggggg)'); }); }); describe('アクセシビリティ検証', () => { it('should not generate warnings for high contrast combinations', () => { // Arrange const highContrastTheme = createHighContrastTheme(); // Act const validation = validator.validateTheme(highContrastTheme); // Assert const contrastWarnings = validation.warnings.filter((w) => w.includes('コントラスト比')); expect(contrastWarnings).toHaveLength(0); }); it('should generate warnings for low contrast combinations', () => { // Arrange const lowContrastTheme = createLowContrastTheme(); // Act const validation = validator.validateTheme(lowContrastTheme); // Assert expect(validation.warnings.length).toBeGreaterThan(0); expect(validation.warnings.some((w) => w.includes('コントラスト比が低すぎます'))).toBe(true); }); it('should check multiple color combinations for accessibility', () => { // Arrange const theme = createValidTheme(); theme.colors.primary_text_color = '#dddddd'; theme.colors.primary_background_color = '#ffffff'; theme.colors.secondary_text_color = '#eeeeee'; theme.colors.secondary_background_color = '#ffffff'; // Act const validation = validator.validateTheme(theme); // Assert const contrastWarnings = validation.warnings.filter((w) => w.includes('コントラスト比')); expect(contrastWarnings.length).toBeGreaterThanOrEqual(1); }); it('should handle accessibility check errors gracefully', () => { // Arrange const themeWithInvalidColors = createValidTheme(); themeWithInvalidColors.colors.primary_text_color = 'invalid'; const validatorNoColorCheck = new ThemeCustomizer({ validateColors: false, checkAccessibility: true, }); // Act const validation = validatorNoColorCheck.validateTheme(themeWithInvalidColors); // Assert expect(validation).toBeDefined(); // エラーが発生してもvalidationオブジェクトは返される }); }); describe('複合検証', () => { it('should handle themes with both color and accessibility issues', () => { // Arrange const problematicTheme = createValidTheme(); problematicTheme.colors.primary_color = 'invalid'; problematicTheme.colors.primary_text_color = '#cccccc'; problematicTheme.colors.primary_background_color = '#ffffff'; // Act const validation = validator.validateTheme(problematicTheme); // Assert expect(validation.isValid).toBe(false); expect(validation.errors.length).toBeGreaterThan(0); expect(validation.warnings.length).toBeGreaterThan(0); }); }); }); describe('キャッシュ機能', () => { let cachedCustomizer: ThemeCustomizer; let nonCachedCustomizer: ThemeCustomizer; beforeEach(() => { cachedCustomizer = new ThemeCustomizer({ enableCache: true }); nonCachedCustomizer = new ThemeCustomizer({ enableCache: false }); }); describe('基本動作', () => { it('should cache result on first generation and hit cache on second', () => { // Arrange const theme = createValidTheme(); // Act const result1 = cachedCustomizer.generateThemedCSS(theme); const result2 = cachedCustomizer.generateThemedCSS(theme); // Assert expect(result1.cacheHit).toBe(false); expect(result2.cacheHit).toBe(true); expect(result2.fullCSS).toBe(result1.fullCSS); }); it('should not hit cache for different themes', () => { // Arrange const theme1 = createValidTheme(); const theme2 = createValidTheme(); theme2.colors.primary_color = '#ff0000'; // Act const result1 = cachedCustomizer.generateThemedCSS(theme1); const result2 = cachedCustomizer.generateThemedCSS(theme2); // Assert expect(result1.cacheHit).toBe(false); expect(result2.cacheHit).toBe(false); expect(result2.fullCSS).not.toBe(result1.fullCSS); }); it('should not use cache when caching is disabled', () => { // Arrange const theme = createValidTheme(); // Act const result1 = nonCachedCustomizer.generateThemedCSS(theme); const result2 = nonCachedCustomizer.generateThemedCSS(theme); // Assert expect(result1.cacheHit).toBe(false); expect(result2.cacheHit).toBe(false); }); it('should clear cache successfully', () => { // Arrange const theme = createValidTheme(); cachedCustomizer.generateThemedCSS(theme); // Act cachedCustomizer.clearCache(); const stats = cachedCustomizer.getCacheStats(); const result = cachedCustomizer.generateThemedCSS(theme); // Assert expect(stats.size).toBe(0); expect(result.cacheHit).toBe(false); }); }); describe('TTL管理', () => { it('should respect cache TTL expiration', () => { // Arrange const theme = createValidTheme(); const shortTTLCustomizer = new ThemeCustomizer({ enableCache: true }); // TTLを短く設定するためにプライベートプロパティにアクセス // 理由: TTL期限切れロジックをテストするため、実行時間内でテスト可能な短い値に設定 // 代替手段: 依存性注入やテスト用設定があれば理想的だが、現在の設計では直接アクセスが最適 // eslint-disable-next-line @typescript-eslint/no-explicit-any const originalTTL = (shortTTLCustomizer as any).CACHE_TTL_MS; // eslint-disable-next-line @typescript-eslint/no-explicit-any (shortTTLCustomizer as any).CACHE_TTL_MS = 1; // 1ms // Act const result1 = shortTTLCustomizer.generateThemedCSS(theme); // Wait for TTL expiration return new Promise((resolve) => { setTimeout(() => { const result2 = shortTTLCustomizer.generateThemedCSS(theme); // Assert expect(result1.cacheHit).toBe(false); expect(result2.cacheHit).toBe(false); // TTL切れでキャッシュミス // Restore original TTL // 理由: テスト後の状態復元のため、変更したプライベートプロパティを元に戻す // eslint-disable-next-line @typescript-eslint/no-explicit-any (shortTTLCustomizer as any).CACHE_TTL_MS = originalTTL; resolve(undefined); }, 10); }); }); }); describe('サイズ制限', () => { it('should respect maximum cache size limit', () => { // Arrange const maxSize = 3; const limitedCustomizer = new ThemeCustomizer({ enableCache: true }); // 理由: キャッシュサイズ制限ロジックをテストするため、テスト用の小さな値に動的変更 // 代替手段: コンストラクタオプションで設定可能にする設計変更が理想的 // eslint-disable-next-line @typescript-eslint/no-explicit-any (limitedCustomizer as any).MAX_CACHE_SIZE = maxSize; // Act - キャッシュサイズを超えるまでテーマを生成 const themes = []; for (let i = 0; i < maxSize + 2; i++) { const theme = createValidTheme(); theme.colors.primary_color = `#${i.toString().padStart(6, '0')}`; themes.push(theme); limitedCustomizer.generateThemedCSS(theme); } // Assert const stats = limitedCustomizer.getCacheStats(); expect(stats.size).toBeLessThanOrEqual(maxSize); expect(stats.maxSize).toBe(maxSize); // 最初のテーマはキャッシュから削除されているはず const firstThemeResult = limitedCustomizer.generateThemedCSS(themes[0]); expect(firstThemeResult.cacheHit).toBe(false); }); it('should provide accurate cache statistics', () => { // Arrange const theme1 = createValidTheme(); const theme2 = createValidTheme(); theme2.colors.primary_color = '#ff0000'; // Act cachedCustomizer.generateThemedCSS(theme1); cachedCustomizer.generateThemedCSS(theme2); const stats = cachedCustomizer.getCacheStats(); // Assert expect(stats.size).toBe(2); expect(stats.maxSize).toBe(100); }); }); }); describe('パフォーマンス', () => { let performanceCustomizer: ThemeCustomizer; beforeEach(() => { performanceCustomizer = new ThemeCustomizer({ enableCache: true, validateColors: true, checkAccessibility: true, }); }); it('should complete CSS generation within 100ms', () => { // Arrange const theme = createValidTheme(); // Act const result = performanceCustomizer.generateThemedCSS(theme); // Assert expect(result.generationTimeMs).toBeLessThan(100); expect(result.generationTimeMs).toBeGreaterThan(0); }); it('should process cache hits significantly faster', () => { // Arrange const theme = createValidTheme(); performanceCustomizer.generateThemedCSS(theme); // 初回実行でキャッシュ // Act const result = performanceCustomizer.generateThemedCSS(theme); // Assert expect(result.cacheHit).toBe(true); expect(result.generationTimeMs).toBeLessThan(10); // キャッシュヒットは10ms以内 }); it('should handle multiple themes efficiently', () => { // Arrange const themes = Array.from({ length: 10 }, (_, i) => { const theme = createValidTheme(); theme.colors.primary_color = `#${i.toString().padStart(6, '0')}`; return theme; }); // Act const startTime = performance.now(); const results = themes.map((theme) => performanceCustomizer.generateThemedCSS(theme)); const totalTime = performance.now() - startTime; // Assert expect(totalTime).toBeLessThan(500); // 10テーマで500ms以内 expect(results).toHaveLength(10); results.forEach((result) => { expect(result.fullCSS).toBeDefined(); }); }); }); describe('エラーハンドリング', () => { describe('バリデーションエラー', () => { it('should return fallback CSS when validation fails', () => { // Arrange const invalidTheme = createInvalidColorTheme(); const validatingCustomizer = new ThemeCustomizer({ validateColors: true }); // Act const result = validatingCustomizer.generateThemedCSS(invalidTheme); // Assert expect(result.fullCSS).toBeDefined(); expect(result.themeCSS).toBe(''); // テーマCSSは空 expect(result.baseCSS).toBeDefined(); // ベースCSSは提供される expect(result.cacheHit).toBe(false); }); it('should generate CSS with invalid colors when validation disabled', () => { // Arrange const invalidTheme = createInvalidColorTheme(); const nonValidatingCustomizer = new ThemeCustomizer({ validateColors: false }); // Act const result = nonValidatingCustomizer.generateThemedCSS(invalidTheme); // Assert expect(result.fullCSS).toBeDefined(); expect(result.themeCSS).toContain('--extracted-primary-color: invalid-color'); }); }); describe('実行時エラー', () => { it('should handle errors during CSS generation gracefully', () => { // Arrange const problematicTheme = createValidTheme(); const customizer = new ThemeCustomizer(); // Mock implementation to force an error // 理由: エラーハンドリングロジックをテストするため、意図的にエラーを発生させる必要 // 代替手段: 依存性注入があれば理想的だが、現在の設計では直接モックが最適 // eslint-disable-next-line @typescript-eslint/no-explicit-any const originalMethod = (customizer as any).combineCSS; // eslint-disable-next-line @typescript-eslint/no-explicit-any (customizer as any).combineCSS = vi.fn(() => { throw new Error('CSS combination failed'); }); // Act const result = customizer.generateThemedCSS(problematicTheme); // Assert expect(result.fullCSS).toBeDefined(); // フォールバックが返される expect(result.themeCSS).toBe(''); // Restore original method // 理由: テスト後の状態復元のため、モックしたメソッドを元に戻す // eslint-disable-next-line @typescript-eslint/no-explicit-any (customizer as any).combineCSS = originalMethod; }); }); describe('フォールバック動作', () => { it('should always return a usable CSS structure even on complete failure', () => { // Arrange const customizer = new ThemeCustomizer({ validateColors: true }); const extremelyInvalidTheme = { // 理由: 完全に無効な構造でのフォールバック動作をテストするため // 代替手段: 個別の無効プロパティテストも可能だが、最悪ケースのテストには全体無効化が必要 // 用途: validateTheme()でObject.entries()が空オブジェクトを処理する際のフォールバック確認 // eslint-disable-next-line @typescript-eslint/no-explicit-any colors: {} as any, // 完全に無効な構造 // eslint-disable-next-line @typescript-eslint/no-explicit-any design: {} as any, // eslint-disable-next-line @typescript-eslint/no-explicit-any brand: {} as any, analysis_notes: '', }; // Act const result = customizer.generateThemedCSS(extremelyInvalidTheme); // Assert expect(result).toBeDefined(); expect(result.fullCSS).toBeDefined(); expect(typeof result.fullCSS).toBe('string'); expect(result.fullCSS.length).toBeGreaterThan(0); }); }); }); describe('デバッグ機能とユーティリティ', () => { let debugCustomizer: ThemeCustomizer; beforeEach(() => { debugCustomizer = new ThemeCustomizer({ enableCache: true, validateColors: true, checkAccessibility: true, }); }); describe('デバッグ情報提供', () => { it('should provide comprehensive debug information', () => { // Arrange const theme = createValidTheme(); // Act const debug = debugCustomizer.debugTheme(theme); // Assert expect(debug.validation).toBeDefined(); expect(debug.validation.isValid).toBeDefined(); expect(debug.cacheKey).toBeDefined(); expect(typeof debug.cacheKey).toBe('string'); expect(debug.generatedVariables).toBeDefined(); expect(debug.generatedVariables).toContain('--extracted-primary-color'); }); it('should provide debug info for invalid themes too', () => { // Arrange const invalidTheme = createInvalidColorTheme(); // Act const debug = debugCustomizer.debugTheme(invalidTheme); // Assert expect(debug.validation).toBeDefined(); expect(debug.validation.isValid).toBe(false); expect(debug.validation.errors.length).toBeGreaterThan(0); expect(debug.cacheKey).toBeDefined(); }); }); describe('キャッシュ統計', () => { it('should provide accurate cache statistics', () => { // Arrange const themes = [createValidTheme(), createHighContrastTheme(), createLowContrastTheme()]; // Act themes.forEach((theme) => debugCustomizer.generateThemedCSS(theme)); const stats = debugCustomizer.getCacheStats(); // Assert expect(stats.size).toBe(3); expect(stats.maxSize).toBeDefined(); expect(stats.maxSize).toBe(100); expect(typeof stats.size).toBe('number'); expect(typeof stats.maxSize).toBe('number'); }); it('should track cache size changes correctly', () => { // Arrange const theme = createValidTheme(); // Act & Assert const initialStats = debugCustomizer.getCacheStats(); expect(initialStats.size).toBe(0); debugCustomizer.generateThemedCSS(theme); const afterGenerationStats = debugCustomizer.getCacheStats(); expect(afterGenerationStats.size).toBe(1); debugCustomizer.clearCache(); const afterClearStats = debugCustomizer.getCacheStats(); expect(afterClearStats.size).toBe(0); }); }); describe('ユーティリティ関数のカバレッジ', () => { it('should handle hash generation for different inputs', () => { // Arrange const theme1 = createValidTheme(); const theme2 = createValidTheme(); theme2.colors.primary_color = '#ff0000'; // Act const debug1 = debugCustomizer.debugTheme(theme1); const debug2 = debugCustomizer.debugTheme(theme2); // Assert expect(debug1.cacheKey).not.toBe(debug2.cacheKey); expect(debug1.cacheKey).toBe(debugCustomizer.debugTheme(theme1).cacheKey); // 同じ入力で同じハッシュ }); it('should handle color utility functions with edge cases', () => { // Arrange const themeWithEdgeCaseColors = createValidTheme(); themeWithEdgeCaseColors.colors.primary_text_color = '#000000'; themeWithEdgeCaseColors.colors.primary_background_color = '#ffffff'; // Act const validation = debugCustomizer.validateTheme(themeWithEdgeCaseColors); // Assert expect(validation).toBeDefined(); // エッジケース(最高コントラスト)でも正常に動作する }); }); describe('プライベートメソッドのテスト(パブリックメソッド経由)', () => { it('should calculate luminance values correctly through contrast ratio check', () => { // Arrange - HEX色の輝度計算をテストするため、既知のコントラスト比を持つ色を使用 const blackOnWhiteTheme = createValidTheme(); blackOnWhiteTheme.colors.primary_text_color = '#000000'; blackOnWhiteTheme.colors.primary_background_color = '#ffffff'; const grayOnWhiteTheme = createValidTheme(); grayOnWhiteTheme.colors.primary_text_color = '#777777'; grayOnWhiteTheme.colors.primary_background_color = '#ffffff'; // Act const blackOnWhiteValidation = debugCustomizer.validateTheme(blackOnWhiteTheme); const grayOnWhiteValidation = debugCustomizer.validateTheme(grayOnWhiteTheme); // Assert - 黒/白は高コントラスト、グレー/白は低コントラスト const blackWarnings = blackOnWhiteValidation.warnings.filter((w) => w.includes('コントラスト比')); const grayWarnings = grayOnWhiteValidation.warnings.filter((w) => w.includes('コントラスト比')); expect(blackWarnings).toHaveLength(0); // 高コントラストなので警告なし expect(grayWarnings.length).toBeGreaterThan(0); // 低コントラストなので警告あり }); it('should convert HEX to RGB correctly through contrast calculation', () => { // Arrange - RGB変換をテストするため、様々なHEX値でテーマを作成 const themes = [ { hex: '#000000', expectHighContrast: true }, { hex: '#ffffff', expectLowContrast: true }, { hex: '#ff0000', expectMediumContrast: true }, { hex: '#cccccc', expectLowContrast: true }, ].map(({ hex, expectHighContrast, expectLowContrast, expectMediumContrast }) => { const theme = createValidTheme(); theme.colors.primary_text_color = hex; theme.colors.primary_background_color = '#ffffff'; return { theme, expectHighContrast, expectLowContrast, expectMediumContrast }; }); // Act & Assert themes.forEach(({ theme }) => { const validation = debugCustomizer.validateTheme(theme); // RGB変換が正常に動作していることを間接的に確認 // (hexToRgb/getLuminanceが正常でないとvalidateThemeが例外を投げる) expect(validation).toBeDefined(); expect(validation.isValid).toBeDefined(); }); }); it('should generate consistent hash values for identical inputs', () => { // Arrange - simpleHashメソッドのテスト const theme = createValidTheme(); // Act - 同じテーマで複数回ハッシュ生成 const debug1 = debugCustomizer.debugTheme(theme); const debug2 = debugCustomizer.debugTheme(theme); const debug3 = debugCustomizer.debugTheme(theme); // Assert - 同じ入力に対して常に同じハッシュが生成される expect(debug1.cacheKey).toBe(debug2.cacheKey); expect(debug2.cacheKey).toBe(debug3.cacheKey); expect(debug1.cacheKey.length).toBeGreaterThan(0); }); it('should generate different hash values for different inputs', () => { // Arrange - 異なるテーマでハッシュ値の違いをテスト const themes = Array.from({ length: 5 }, (_, i) => { const theme = createValidTheme(); theme.colors.primary_color = `#${i.toString().padStart(6, '0')}`; return theme; }); // Act const hashes = themes.map((theme) => debugCustomizer.debugTheme(theme).cacheKey); // Assert - 全て異なるハッシュ値が生成される const uniqueHashes = new Set(hashes); expect(uniqueHashes.size).toBe(themes.length); }); }); describe('境界値テストケース', () => { it('should handle boundary value theme data', () => { // Arrange const boundaryTheme = createBoundaryValueTheme(); // Act const result = debugCustomizer.generateThemedCSS(boundaryTheme); const validation = debugCustomizer.validateTheme(boundaryTheme); // Assert expect(result).toBeDefined(); expect(validation).toBeDefined(); // 境界値でも処理が完了する }); it('should handle performance stress test with large data', () => { // Arrange const performanceTheme = createPerformanceTestTheme(); const customizer = new ThemeCustomizer({ validateColors: false, // パフォーマンステストなのでバリデーション無効 checkAccessibility: false, }); // Act const startTime = performance.now(); const result = customizer.generateThemedCSS(performanceTheme); const endTime = performance.now(); // Assert expect(result).toBeDefined(); expect(result.fullCSS).toBeDefined(); expect(endTime - startTime).toBeLessThan(1000); // 大量データでも1秒以内 }); }); }); });