mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 09:19:34 +05:30
feat(ascii-art): figlet adapter with lazy load, font cache, structured error
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Figlet adapter — lazy-loads the figlet package and caches the font list.
|
||||
*
|
||||
* The figlet npm package ships ~400 fonts. Lazy-loading on first
|
||||
* `generateFiglet` / `listFigletFonts` call keeps app startup fast. If
|
||||
* `require('figlet')` itself fails (corrupt install), the adapter reports
|
||||
* the system as unavailable instead of crashing.
|
||||
*
|
||||
* @module AsciiArt.figlet-adapter
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
class AsciiArtFigletError extends Error {
|
||||
constructor(message, cause) {
|
||||
super(message);
|
||||
this.name = 'AsciiArtFigletError';
|
||||
this.cause = cause;
|
||||
}
|
||||
}
|
||||
|
||||
let _figlet = undefined; // undefined = not yet attempted; null = failed
|
||||
let _figletFonts = undefined; // undefined = not yet listed
|
||||
|
||||
function loadFiglet() {
|
||||
if (_figlet !== undefined) return _figlet;
|
||||
try {
|
||||
_figlet = require('figlet');
|
||||
} catch {
|
||||
_figlet = null;
|
||||
}
|
||||
return _figlet;
|
||||
}
|
||||
|
||||
function listFigletFonts() {
|
||||
const f = loadFiglet();
|
||||
if (!f) return [];
|
||||
if (_figletFonts === undefined) {
|
||||
try {
|
||||
_figletFonts = f.fontsSync();
|
||||
} catch {
|
||||
_figletFonts = [];
|
||||
}
|
||||
}
|
||||
return _figletFonts.slice();
|
||||
}
|
||||
|
||||
function generateFiglet(text, font) {
|
||||
const f = loadFiglet();
|
||||
if (!f) {
|
||||
throw new AsciiArtFigletError('figlet is not available');
|
||||
}
|
||||
try {
|
||||
return f.textSync(String(text ?? ''), { font });
|
||||
} catch (err) {
|
||||
throw new AsciiArtFigletError(`figlet render failed for font "${font}": ${err.message}`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// Test-only cache reset; not used at runtime.
|
||||
function _resetCache() {
|
||||
_figlet = undefined;
|
||||
_figletFonts = undefined;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AsciiArtFigletError,
|
||||
loadFiglet,
|
||||
listFigletFonts,
|
||||
generateFiglet,
|
||||
_resetCache,
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*
|
||||
* Adapter tests — figlet is mocked so we don't ship 400 KB of fonts into
|
||||
* the test runner.
|
||||
*/
|
||||
jest.mock('figlet', () => {
|
||||
const actual = jest.requireActual('figlet');
|
||||
return {
|
||||
__esModule: true,
|
||||
default: actual,
|
||||
textSync: jest.fn(() => 'MOCKED_FIGLET_OUTPUT'),
|
||||
fontsSync: jest.fn(() => ['Standard', 'Big', 'Slant']),
|
||||
};
|
||||
});
|
||||
|
||||
const FigletAdapter = require('../../src/main/AsciiArt.figlet-adapter');
|
||||
|
||||
describe('AsciiArt.figlet-adapter', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
FigletAdapter._resetCache(); // test-only reset
|
||||
});
|
||||
|
||||
test('loadFiglet returns the figlet module on first call', () => {
|
||||
const mod = FigletAdapter.loadFiglet();
|
||||
expect(mod).not.toBeNull();
|
||||
expect(typeof mod.textSync).toBe('function');
|
||||
});
|
||||
|
||||
test('listFigletFonts returns string[] from figlet.fontsSync()', () => {
|
||||
const fonts = FigletAdapter.listFigletFonts();
|
||||
expect(fonts).toEqual(['Standard', 'Big', 'Slant']);
|
||||
});
|
||||
|
||||
test('listFigletFonts caches between calls', () => {
|
||||
FigletAdapter.listFigletFonts();
|
||||
FigletAdapter.listFigletFonts();
|
||||
const figlet = require('figlet');
|
||||
expect(figlet.fontsSync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('generateFiglet returns rendered string', () => {
|
||||
const out = FigletAdapter.generateFiglet('HELLO', 'Big');
|
||||
expect(out).toBe('MOCKED_FIGLET_OUTPUT');
|
||||
const figlet = require('figlet');
|
||||
expect(figlet.textSync).toHaveBeenCalledWith('HELLO', { font: 'Big' });
|
||||
});
|
||||
|
||||
test('generateFiglet throws AsciiArtFigletError when figlet throws', () => {
|
||||
const figlet = require('figlet');
|
||||
figlet.textSync.mockImplementationOnce(() => {
|
||||
throw new Error('unknown font');
|
||||
});
|
||||
expect(() => FigletAdapter.generateFiglet('X', 'Nope')).toThrow(
|
||||
FigletAdapter.AsciiArtFigletError
|
||||
);
|
||||
});
|
||||
|
||||
test('returns null when figlet module fails to load', () => {
|
||||
// simulate require failure by reloading after breaking the module cache
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('figlet', () => {
|
||||
throw new Error('not found');
|
||||
});
|
||||
const Fresh = require('../../src/main/AsciiArt.figlet-adapter');
|
||||
Fresh._resetCache();
|
||||
expect(Fresh.loadFiglet()).toBeNull();
|
||||
expect(Fresh.listFigletFonts()).toEqual([]);
|
||||
expect(() => Fresh.generateFiglet('X', 'Big')).toThrow(Fresh.AsciiArtFigletError);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user