diff --git a/src/accentFolding.js b/src/accentFolding.js index bad52c3..d2a8e68 100644 --- a/src/accentFolding.js +++ b/src/accentFolding.js @@ -23,31 +23,47 @@ class AccentFolding { } replace(text) { + if (typeof text !== 'string') { + throw new TypeError('Input must be a string'); + } return [...text].map((char) => this.#accentMap.get(char) || char).join(''); } highlightMatch(str, fragment, wrapTag = 'b') { - if (!fragment) return str; + try { + if (!fragment) return str; - const escapedFragment = fragment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const strFolded = this.#fold(str).toLowerCase(); - const fragmentFolded = this.#fold(escapedFragment).toLowerCase(); + if (typeof str !== 'string' || typeof fragment !== 'string') { + throw new TypeError('Both str and fragment must be strings'); + } - const re = new RegExp(fragmentFolded, 'g'); - let result = ''; - let lastIndex = 0; - let hasMatch = false; + if (typeof wrapTag !== 'string') { + throw new TypeError('wrapTag must be a string'); + } - strFolded.replace(re, (match, index) => { - hasMatch = true; - result += this.#escapeHtml(str.slice(lastIndex, index)); - result += `<${wrapTag}>${this.#escapeHtml(str.slice(index, index + match.length))}`; - lastIndex = index + match.length; - }); + const escapedFragment = fragment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const strFolded = this.#fold(str).toLowerCase(); + const fragmentFolded = this.#fold(escapedFragment).toLowerCase(); - result += this.#escapeHtml(str.slice(lastIndex)); + const re = new RegExp(fragmentFolded, 'g'); + let result = ''; + let lastIndex = 0; + let hasMatch = false; - return hasMatch ? result : str; + strFolded.replace(re, (match, index) => { + hasMatch = true; + result += this.#escapeHtml(str.slice(lastIndex, index)); + result += `<${wrapTag}>${this.#escapeHtml(str.slice(index, index + match.length))}`; + lastIndex = index + match.length; + }); + + result += this.#escapeHtml(str.slice(lastIndex)); + + return hasMatch ? result : str; + } catch (error) { + console.error('Error in highlightMatch:', error.message); + throw error; // Return original string if there's an error + } } #escapeHtml(unsafe) { diff --git a/src/accentFolding.js.test.js b/src/accentFolding.js.test.js index 6dd896c..61307eb 100644 --- a/src/accentFolding.js.test.js +++ b/src/accentFolding.js.test.js @@ -1,11 +1,38 @@ -import { expect, describe, it } from 'vitest'; +import { expect, describe, it, beforeEach } from 'vitest'; import AccentFolding from './accentFolding.js'; import accentMap from './accentMap.json'; describe('AccentFolding', () => { - const accentFolder = new AccentFolding(); + let accentFolder; + + beforeEach(() => { + accentFolder = new AccentFolding(); + }); describe('highlightMatch', () => { + it('should throw TypeError if str is not a string', () => { + expect(() => accentFolder.highlightMatch(123, 'test')).toThrow(TypeError); + expect(() => accentFolder.highlightMatch(123, 'test')).toThrow( + 'Both str and fragment must be strings' + ); + }); + + it('should throw TypeError if fragment is not a string', () => { + expect(() => accentFolder.highlightMatch('test', 123)).toThrow(TypeError); + expect(() => accentFolder.highlightMatch('test', 123)).toThrow( + 'Both str and fragment must be strings' + ); + }); + + it('should throw TypeError if wrapTag is not a string', () => { + expect(() => accentFolder.highlightMatch('test', 'es', 123)).toThrow( + TypeError + ); + expect(() => accentFolder.highlightMatch('test', 'es', 123)).toThrow( + 'wrapTag must be a string' + ); + }); + it('should recognize simple accents', () => { expect(accentFolder.highlightMatch('Fulanilo López', 'lo')).toBe( 'Fulanilo pez' @@ -63,6 +90,11 @@ describe('AccentFolding', () => { }); describe('replace', () => { + it('should throw TypeError if input is not a string', () => { + expect(() => accentFolder.replace(123)).toThrow(TypeError); + expect(() => accentFolder.replace(123)).toThrow('Input must be a string'); + }); + it.each(Object.entries(accentMap))( 'should replace %s with %s', (accentedChar, expectedChar) => {