import { Component, input, inputBinding, signal, WritableSignal } from '@angular/core'; import { LucideIcon } from './lucide-icon'; import { LucideIconData, LucideIconInput } from './types'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideLucideIcons } from './lucide-icons'; import { LucideActivity } from './icons/activity'; import { By } from '@angular/platform-browser'; @Component({ template: `@if (icon(); as iconData) { }`, imports: [LucideIcon], }) class TestHostComponent { readonly icon = input(); } describe('LucideIcon', () => { let component: LucideIcon; let fixture: ComponentFixture; let icon: WritableSignal; let name: WritableSignal; let title: WritableSignal; let color: WritableSignal; let size: WritableSignal; let strokeWidth: WritableSignal; let absoluteStrokeWidth: WritableSignal; const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr); const testIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]]; const testIcon2: LucideIconData = [ ['circle', { cx: 12, cy: 12, r: 8 }], ['polyline', { points: '1 1 22 22' }], ]; beforeEach(async () => { TestBed.configureTestingModule({ providers: [provideLucideIcons({ demo: testIcon })], }); icon = signal('demo'); name = signal(undefined); title = signal(undefined); color = signal(undefined); size = signal(undefined); strokeWidth = signal(undefined); absoluteStrokeWidth = signal(undefined); fixture = TestBed.createComponent(LucideIcon, { inferTagName: true, bindings: [ inputBinding('lucideIcon', icon), inputBinding('name', name), inputBinding('title', title), inputBinding('color', color), inputBinding('size', size), inputBinding('strokeWidth', strokeWidth), inputBinding('absoluteStrokeWidth', absoluteStrokeWidth), ], }); component = fixture.componentInstance; }); it('should create', () => { fixture.detectChanges(); expect(component).toBeTruthy(); }); it('should render children', () => { icon.set(testIcon2); fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).toBe( '', ); }); it('should remove children on change', () => { icon.set(null); fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).toBe(''); }); describe('iconInput', () => { it('should support LucideIconData input', () => { icon.set(testIcon); name.set('custom-name'); fixture.detectChanges(); expect(component.iconData()).toBe(testIcon); expect(component.iconName()).toBe('custom-name'); expect(fixture.nativeElement.innerHTML).toBe( '', ); }); it('should support LucideIconComponentType input', () => { icon.set(LucideActivity); fixture.detectChanges(); expect(component.iconData()).toBe(LucideActivity.iconData); expect(component.iconName()).toBe(LucideActivity.iconName); }); it('should support string icon name', () => { icon.set('demo'); fixture.detectChanges(); expect(component.iconData()).toBe(testIcon); expect(component.iconName()).toBe('demo'); }); it('should throw error if no icon founds', () => { icon.set('invalid'); expect(() => fixture.detectChanges()).toThrowError(`Unable to resolve icon 'invalid'`); }); }); describe('class', () => { it('should add all classes', () => { fixture.detectChanges(); expect(getSvgAttribute('class')).toBe('lucide lucide-demo'); }); it('should add class from name, even if icon has name', () => { icon.set(LucideActivity); name.set('custom-name'); fixture.detectChanges(); expect(getSvgAttribute('class')).toBe('lucide lucide-custom-name'); }); it('should add class icon if available', () => { icon.set(LucideActivity); fixture.detectChanges(); expect(getSvgAttribute('class')).toBe('lucide lucide-activity'); }); it('should remove class on change', () => { icon.set(null); fixture.detectChanges(); expect(getSvgAttribute('class')).toBe('lucide'); }); }); describe('color', () => { it('should default to currentColor', () => { fixture.detectChanges(); expect(getSvgAttribute('stroke')).toBe('currentColor'); }); it('should set color', () => { color.set('red'); fixture.detectChanges(); expect(getSvgAttribute('stroke')).toBe('red'); }); }); describe('size', () => { it('should default to 24', () => { fixture.detectChanges(); expect(getSvgAttribute('width')).toBe('24'); expect(getSvgAttribute('height')).toBe('24'); }); it('should set size', () => { size.set(12); fixture.detectChanges(); expect(getSvgAttribute('width')).toBe('12'); expect(getSvgAttribute('height')).toBe('12'); }); it('should allow string size', () => { size.set('18'); fixture.detectChanges(); expect(getSvgAttribute('width')).toBe('18'); expect(getSvgAttribute('height')).toBe('18'); }); it('should use default on invalid string', () => { size.set('large'); fixture.detectChanges(); expect(getSvgAttribute('width')).toBe('24'); expect(getSvgAttribute('height')).toBe('24'); }); }); describe('strokeWidth', () => { it('should default to 2', () => { fixture.detectChanges(); expect(getSvgAttribute('stroke-width')).toBe('2'); }); it('should set stroke width', () => { strokeWidth.set(1.41); fixture.detectChanges(); expect(getSvgAttribute('stroke-width')).toBe('1.41'); }); it('should allow string stroke width', () => { strokeWidth.set('1px'); fixture.detectChanges(); expect(getSvgAttribute('stroke-width')).toBe('1'); }); }); describe('absoluteStrokeWidth', () => { it('should not adjust stroke width', () => { strokeWidth.set(2); size.set(12); absoluteStrokeWidth.set(false); fixture.detectChanges(); expect(getSvgAttribute('stroke-width')).toBe('2'); }); it('should adjust stroke width', () => { strokeWidth.set(2); size.set(12); absoluteStrokeWidth.set(true); fixture.detectChanges(); expect(getSvgAttribute('stroke-width')).toBe('4'); }); }); describe('title', () => { it('should set title if provided', () => { title.set('Foobar'); fixture.detectChanges(); const titleEl = fixture.debugElement.query(By.css('title')).nativeElement; expect(titleEl).toBeDefined(); expect(titleEl.textContent).toBe('Foobar'); }); it('should not set aria-hidden when title is set', () => { title.set('Foobar'); fixture.detectChanges(); expect(getSvgAttribute('aria-hidden')).toBeUndefined; }); it('should set aria-hidden if no title is provided', () => { title.set(undefined); fixture.detectChanges(); expect(getSvgAttribute('aria-hidden')).toBeUndefined; }); }); describe('content projection', () => { it('should project content', () => { const hostFixture = TestBed.createComponent(TestHostComponent); hostFixture.componentRef.setInput('icon', testIcon); hostFixture.detectChanges(); hostFixture.componentRef.setInput('icon', testIcon2); hostFixture.detectChanges(); const rect = hostFixture.debugElement.query(By.css('rect')).nativeElement; expect(rect).toBeInstanceOf(SVGElement); expect(rect.outerHTML).toBe(''); }); }); });