// Load the JIT compiler BEFORE any Angular router/common imports import '@angular/compiler'; import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import { Injector } from '@angular/core'; import { ThemeService } from './theme.service'; import { StationConfig } from '../interfaces/station-config'; describe('ThemeService', () => { let service: ThemeService; const mockConfig: StationConfig = { id: 1, key: 'default', callsign: 'KMOF', name_primary: 'KMountain', name_secondary: 'Flower Radio', tagline: 'Community Radio', frequency: '91.7', founded_year: 2020, nonprofit_type: '501c3', ein: '12-3456789', logo_url: 'https://kmtnflower.org/logo.png', hero_background_url: '', hero_icon_url: '', hero_divider_url: '', address: '', phone: '', email: 'info@kmtnflower.org', website: 'kmtnflower.org', donation_url: '', stream_url: '', stream_metadata_url: '', play_store_icon_url: '', play_store_url: '', app_store_embed_html: '', social_facebook: '', social_twitter: '', social_instagram: '', social_youtube: '', hero_subtitle: 'Your community-powered radio station.', about_intro: 'About KMountain Flower Radio.', about_donation_text: '', schedule_intro: 'Our weekly schedule.', events_intro: 'Community events.', color_primary: '#4a90d9', color_primary_light: '#6ba3eb', color_primary_dark: '#2e6faa', color_primary_muted: '#7baee7', color_accent: '#e85d3a', color_accent_light: '#eb7d5e', color_accent_dark: '#c94422', color_accent_muted: '#e98a72', color_background: '#faf6f0', color_text: '#333333', color_success: '#4caf50', color_danger: '#f44336', color_info: '#2196f3', color_white: '#ffffff', color_light: '#eeeeee', color_medium: '#9e9e9e', color_black: '#000000', } as const as StationConfig; beforeEach(() => { const injector = Injector.create({ providers: [ThemeService], }); service = injector.get(ThemeService); }); afterEach(() => { // Clear all inline CSS custom properties after each test. const root = document.documentElement.style; for (const { prop } of [ { prop: '--color-primary' }, { prop: '--color-primary-light' }, { prop: '--color-primary-dark' }, { prop: '--color-primary-muted' }, { prop: '--color-accent' }, { prop: '--color-accent-light' }, { prop: '--color-accent-dark' }, { prop: '--color-accent-muted' }, { prop: '--color-cream' }, { prop: '--color-dark' }, { prop: '--color-success' }, { prop: '--color-danger' }, { prop: '--color-info' }, { prop: '--color-white' }, { prop: '--color-light' }, { prop: '--color-medium' }, { prop: '--color-black' }, { prop: '--sky-gradient' }, { prop: '--sunset-gradient' }, { prop: '--mixed-gradient' }, ]) { root.removeProperty(prop); } }); it('should apply all CSS color variables', () => { const setPropertySpy = vi.spyOn(document.documentElement.style, 'setProperty'); service.apply(mockConfig); // 17 color fields + 3 gradient fields = 20 setProperty calls expect(setPropertySpy).toHaveBeenCalledTimes(20); }); it('should set primary color CSS variable', () => { service.apply(mockConfig); const rootStyle = document.documentElement.style; expect(rootStyle.getPropertyValue('--color-primary')).toBe('#4a90d9'); }); it('should set accent color CSS variable', () => { service.apply(mockConfig); const rootStyle = document.documentElement.style; expect(rootStyle.getPropertyValue('--color-accent')).toBe('#e85d3a'); }); it('should set background color to --color-cream', () => { service.apply(mockConfig); const rootStyle = document.documentElement.style; expect(rootStyle.getPropertyValue('--color-cream')).toBe('#faf6f0'); }); it('should derive --sky-gradient from primary colors', () => { service.apply(mockConfig); const rootStyle = document.documentElement.style; const sky = rootStyle.getPropertyValue('--sky-gradient'); expect(sky).toContain('linear-gradient(135deg'); expect(sky).toContain('#2e6faa'); expect(sky).toContain('#4a90d9'); expect(sky).toContain('#6ba3eb'); }); it('should derive --sunset-gradient from accent colors', () => { service.apply(mockConfig); const rootStyle = document.documentElement.style; const sunset = rootStyle.getPropertyValue('--sunset-gradient'); expect(sunset).toContain('linear-gradient(135deg'); expect(sunset).toContain('#c94422'); expect(sunset).toContain('#e85d3a'); expect(sunset).toContain('#eb7d5e'); }); it('should derive --mixed-gradient from primary and accent', () => { service.apply(mockConfig); const rootStyle = document.documentElement.style; const mixed = rootStyle.getPropertyValue('--mixed-gradient'); expect(mixed).toContain('linear-gradient(135deg'); expect(mixed).toContain('#2e6faa'); expect(mixed).toContain('#4a90d9'); expect(mixed).toContain('#e85d3a'); }); it('should work on both browser and server platforms (no platform check)', () => { // ThemeService no longer guards on isPlatformBrowser — it applies CSS vars // during SSR so the server-rendered HTML carries the correct theme. service.apply(mockConfig); const rootStyle = document.documentElement.style; expect(rootStyle.getPropertyValue('--color-primary')).toBe('#4a90d9'); }); it('should skip fields with empty string values', () => { const emptyConfig = { ...mockConfig, color_primary: '' } as StationConfig; const setPropertySpy = vi.spyOn(document.documentElement.style, 'setProperty'); service.apply(emptyConfig); expect(setPropertySpy).not.toHaveBeenCalledWith('--color-primary', ''); }); });