test(lucide): add tests for replaceElement and edge cases (#5059)

Co-authored-by: grzdev <grzdev@users.noreply.github.com>
This commit is contained in:
Dami Oyeniyi
2026-10-23 18:19:29 +02:00
committed by GitHub
co-authored by grzdev
parent 1ea91c0b72
commit 49161aba73
+114 -9
View File
@@ -1,8 +1,14 @@
import { describe, it, expect } from 'vitest';
import { getAttrs, getClassNames } from '../src/replaceElement';
import { describe, it, expect, vi } from 'vitest';
import replaceElement, { getAttrs, getClassNames } from '../src/replaceElement';
import type { IconNode } from '../src/types';
describe('getAtts', () => {
it('should returns attrbrutes of an element', () => {
const testIconNode: IconNode = [
['polygon', { points: '11 5 6 9 2 9 2 15 6 15 11 19 11 5' }],
['path', { d: 'M15.54 8.46a5 5 0 0 1 0 7.07' }],
];
describe('getAttrs', () => {
it('should return attributes of an element', () => {
const element = {
attributes: [
{
@@ -10,7 +16,7 @@ describe('getAtts', () => {
value: 'item1 item2 item4',
},
{
name: 'date-name',
name: 'data-name',
value: 'volume',
},
],
@@ -19,25 +25,124 @@ describe('getAtts', () => {
const attrs = getAttrs(element as unknown as Element);
expect(attrs.class).toBe(element.attributes[0].value);
expect(attrs['data-name']).toBe('volume');
});
});
describe('getClassNames', () => {
it('should returns an array when giving class property of string', () => {
it('should return an array when given class property of string', () => {
const elementAttrs = {
class: 'item1 item2 item3',
};
const attrs = getClassNames(elementAttrs);
expect(JSON.stringify(attrs)).toBe(JSON.stringify(['item1', 'item2', 'item3']));
expect(attrs).toEqual(['item1', 'item2', 'item3']);
});
it('should returns an array when givind class property with an array', () => {
it('should return an array when given class property with an array', () => {
const elementAttrs = {
class: ['item1', 'item2', 'item3'],
};
const attrs = getClassNames(elementAttrs);
expect(JSON.stringify(attrs)).toBe(JSON.stringify(['item1', 'item2', 'item3']));
expect(attrs).toEqual(['item1', 'item2', 'item3']);
});
it('should return string when given a string directly', () => {
expect(getClassNames('custom-class')).toBe('custom-class');
});
it('should return empty string when no class property exists', () => {
expect(getClassNames({})).toBe('');
});
});
describe('replaceElement', () => {
it('should return undefined if element lacks the name attribute', () => {
const element = document.createElement('i');
const result = replaceElement(element, {
nameAttr: 'data-lucide',
icons: { Volume2: testIconNode },
attrs: {},
});
expect(result).toBeUndefined();
});
it('should warn and return undefined when icon is not found in icons object', () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const element = document.createElement('i');
element.setAttribute('data-lucide', 'non-existent');
document.body.appendChild(element);
const result = replaceElement(element, {
nameAttr: 'data-lucide',
icons: {},
attrs: {},
});
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining('icon name was not found in the provided icons object'),
);
expect(result).toBeUndefined();
element.remove();
warnSpy.mockRestore();
});
it('should replace element with svg in parentNode and forward attributes', () => {
const container = document.createElement('div');
const element = document.createElement('i');
element.setAttribute('data-lucide', 'volume-2');
element.setAttribute('class', 'custom-icon');
container.appendChild(element);
const replaced = replaceElement(element, {
nameAttr: 'data-lucide',
icons: { Volume2: testIconNode },
attrs: { stroke: 'red' },
});
expect(replaced).toBe(element);
expect(container.querySelector('i')).toBeNull();
const svg = container.querySelector('svg');
expect(svg).not.toBeNull();
expect(svg?.getAttribute('data-lucide')).toBe('volume-2');
expect(svg?.getAttribute('stroke')).toBe('red');
expect(svg?.getAttribute('aria-hidden')).toBe('true');
expect(svg?.classList.contains('lucide-volume-2')).toBe(true);
expect(svg?.classList.contains('custom-icon')).toBe(true);
});
it('should not add aria-hidden if element has accessibility attributes', () => {
const container = document.createElement('div');
const element = document.createElement('i');
element.setAttribute('data-lucide', 'volume-2');
element.setAttribute('aria-label', 'Volume Control');
container.appendChild(element);
replaceElement(element, {
nameAttr: 'data-lucide',
icons: { Volume2: testIconNode },
attrs: {},
});
const svg = container.querySelector('svg');
expect(svg?.getAttribute('aria-hidden')).toBeNull();
expect(svg?.getAttribute('aria-label')).toBe('Volume Control');
});
it('should handle detached elements without parentNode gracefully', () => {
const element = document.createElement('i');
element.setAttribute('data-lucide', 'volume-2');
const result = replaceElement(element, {
nameAttr: 'data-lucide',
icons: { Volume2: testIconNode },
attrs: {},
});
expect(result).toBeUndefined();
});
});