const {expect} = require('chai'); const Filter = require('../lib/ansi_to_html.js'); function test(text, result, done, opts) { if (!opts) { opts = {}; } const f = new Filter(opts); function filtered(memo, t) { return memo + f.toHtml(t); } text = typeof text.reduce === 'function' ? text : [text]; expect(text.reduce(filtered, '')).to.equal(result); return done(); } describe('ansi to html', function () { describe('constructed with no options', function () { it('doesn\'t modify the input string', function (done) { const text = 'some text'; const result = 'some text'; return test(text, result, done); }); it('returns plain text when given plain text with linefeed', function (done) { const text = 'test\ntest\n'; const result = 'test\ntest\n'; return test(text, result, done); }); it('returns plain text when given plain text with CR & LF', function (done) { const text = 'testCRLF\r\ntest'; const result = 'testCRLF\r\ntest'; return test(text, result, done); }); it('returns plain text when given plain text with multi CR', function (done) { const text = 'testCRLF\r\r\r\ntest'; const result = 'testCRLF\r\r\r\ntest'; return test(text, result, done); }); it('renders foreground colors', function (done) { const text = 'colors: \x1b[30mblack\x1b[37mwhite'; const result = 'colors: blackwhite'; return test(text, result, done); }); it('renders light foreground colors', function (done) { const text = 'colors: \x1b[90mblack\x1b[97mwhite'; const result = 'colors: blackwhite'; return test(text, result, done); }); it('renders background colors', function (done) { const text = 'colors: \x1b[40mblack\x1b[47mwhite'; const result = 'colors: blackwhite'; return test(text, result, done); }); it('renders light background colors', function (done) { const text = 'colors: \x1b[100mblack\x1b[107mwhite'; const result = 'colors: blackwhite'; return test(text, result, done); }); it('renders strikethrough', function (done) { const text = 'strike: \x1b[9mthat'; const result = 'strike: that'; return test(text, result, done); }); it('renders blink', function (done) { const text = 'blink: \x1b[5mwhat'; const result = 'blink: what'; return test(text, result, done); }); it('renders underline', function (done) { const text = 'underline: \x1b[4mstuff'; const result = 'underline: stuff'; return test(text, result, done); }); it('renders bold', function (done) { const text = 'bold: \x1b[1mstuff'; const result = 'bold: stuff'; return test(text, result, done); }); it('renders italic', function (done) { const text = 'italic: \x1b[3mstuff'; const result = 'italic: stuff'; return test(text, result, done); }); it('handles resets', function (done) { const text = '\x1b[1mthis is bold\x1b[0m, but this isn\'t'; const result = 'this is bold, but this isn\'t'; return test(text, result, done); }); it('handles multiple resets', function (done) { const text = 'normal, \x1b[1mbold, \x1b[4munderline, \x1b[31mred\x1b[0m, normal'; const result = 'normal, bold, underline, red, normal'; return test(text, result, done); }); it('handles resets with implicit 0', function (done) { const text = '\x1b[1mthis is bold\x1b[m, but this isn\'t'; const result = 'this is bold, but this isn\'t'; return test(text, result, done); }); it('renders multi-attribute sequences', function (done) { const text = 'normal, \x1b[1;4;31mbold, underline, and red\x1b[0m, normal'; const result = 'normal, bold, underline,' + ' and red, normal'; return test(text, result, done); }); it('renders multi-attribute sequences with a semi-colon', function (done) { const text = 'normal, \x1b[1;4;31;mbold, underline, and red\x1b[0m, normal'; const result = 'normal, bold, underline, and red, normal'; return test(text, result, done); }); it('eats malformed sequences', function (done) { const text = '\x1b[25oops forgot the \'m\''; const result = 'oops forgot the \'m\''; return test(text, result, done); }); it('renders xterm 256 sequences', function (done) { const text = '\x1b[38;5;196mhello'; const result = 'hello'; return test(text, result, done); }); it('renders foreground rgb sequences', function (done) { const text = '\x1b[38;2;210;60;114mhello'; const result = 'hello'; return test(text, result, done); }); it('renders background rgb sequences', function (done) { const text = '\x1b[48;2;155;42;45mhello'; const result = 'hello'; return test(text, result, done); }); it('handles resetting to default foreground color', function (done) { const text = '\x1b[30mblack\x1b[39mdefault'; const result = 'blackdefault'; return test(text, result, done); }); it('handles resetting to default background color', function (done) { const text = '\x1b[100mblack\x1b[49mdefault'; const result = 'blackdefault'; return test(text, result, done); }); it('is able to disable underline', function (done) { const text = 'underline: \x1b[4mstuff\x1b[24mthings'; const result = 'underline: stuffthings'; return test(text, result, done); }); it('is able to skip disabling underline', function (done) { const text = 'not underline: stuff\x1b[24mthings'; const result = 'not underline: stuffthings'; return test(text, result, done); }); it('renders two escape sequences in sequence', function (done) { const text = 'months remaining\x1b[1;31mtimes\x1b[m\x1b[1;32mmultiplied by\x1b[m $10'; const result = 'months remainingtimesmultiplied by $10'; return test(text, result, done); }); it('drops EL code with no parameter', function (done) { const text = '\x1b[Khello'; const result = 'hello'; return test(text, result, done); }); it('drops EL code with 0 parameter', function (done) { const text = '\x1b[0Khello'; const result = 'hello'; return test(text, result, done); }); it('drops EL code with 0 parameter after new line character', function (done) { const text = 'HELLO\n\x1b[0K\u001b[33;1mWORLD\u001b[0m\n'; const result = 'HELLO\nWORLD\n'; return test(text, result, done); }); it('drops EL code with 1 parameter', function (done) { const text = '\x1b[1Khello'; const result = 'hello'; return test(text, result, done); }); it('drops EL code with 2 parameter', function (done) { const text = '\x1b[2Khello'; const result = 'hello'; return test(text, result, done); }); it('drops ED code with 0 parameter', function (done) { const text = '\x1b[Jhello'; const result = 'hello'; return test(text, result, done); }); it('drops ED code with 1 parameter', function (done) { const text = '\x1b[1Jhello'; const result = 'hello'; return test(text, result, done); }); it('drops HVP code with 0 parameter', function (done) { const text = '\x1b[;fhello'; const result = 'hello'; return test(text, result, done); }); it('drops HVP code with 1 parameter', function (done) { const text = '\x1b[123;fhello'; const result = 'hello'; return test(text, result, done); }); it('drops HVP code with 2 parameter', function (done) { const text = '\x1b[123;456fhello'; const result = 'hello'; return test(text, result, done); }); it('drops setusg0 sequence', function (done) { const text = '\x1b[(Bhello'; const result = 'hello'; return test(text, result, done); }); it('renders un-italic code appropriately', function (done) { const text = '\x1b[3mHello\x1b[23m World'; const result = 'Hello World'; return test(text, result, done); }); it('skips rendering un-italic code appropriately', function (done) { const text = 'Hello\x1b[23m World'; const result = 'Hello World'; return test(text, result, done); }); it('renders overline', function (done) { const text = '\x1b[53mHello World'; const result = 'Hello World'; return test(text, result, done); }); it('renders normal text', function (done) { const text = '\x1b[22mnormal text'; const result = 'normal text'; return test(text, result, done); }); }); describe('with escapeXML option enabled', function () { it('escapes XML entities', function (done) { const text = 'normal, \x1b[1;4;31;mbold, , and red\x1b[0m, normal'; const result = 'normal, bold, <underline>, and red, normal'; return test(text, result, done, {escapeXML: true}); }); }); describe('with newline option enabled', function () { it('renders line breaks', function (done) { const text = 'test\ntest\n'; const result = 'test
test
'; return test(text, result, done, {newline: true}); }); it('renders windows styled line breaks (CR+LF)', function (done) { const text = 'testCRLF\r\ntestLF'; const result = 'testCRLF
testLF'; return test(text, result, done, {newline: true}); }); it('renders windows styled line breaks (multi CR+LF)', function (done) { const text = 'testCRLF\r\r\r\ntestLF'; const result = 'testCRLF
testLF'; return test(text, result, done, {newline: true}); }); it('renders multiple line breaks', function (done) { const text = 'test\n\ntest\n'; const result = 'test

test
'; return test(text, result, done, {newline: true}); }); }); describe('with stream option enabled', function () { it('persists styles between toHtml() invocations', function (done) { const text = ['\x1b[31mred', 'also red']; const result = 'redalso red'; return test(text, result, done, {stream: true}); }); it('persists styles between more than two toHtml() invocations', function (done) { const text = ['\x1b[31mred', 'also red', 'and red']; const result = 'redalso redand red'; return test(text, result, done, {stream: true}); }); it('does not persist styles beyond their usefulness', function (done) { const text = ['\x1b[31mred', 'also red', '\x1b[30mblack', 'and black']; const result = 'redalso redblackand black'; return test(text, result, done, {stream: true}); }); it('removes one state when encountering a reset', function (done) { const text = ['\x1b[1mthis is bold\x1b[0m, but this isn\'t', ' nor is this']; const result = 'this is bold, but this isn\'t nor is this'; return test(text, result, done, {stream: true}); }); it('removes multiple state when encountering a reset', function (done) { const text = ['\x1b[1mthis \x1b[9mis bold\x1b[0m, but this isn\'t', ' nor is this']; const result = 'this is bold, but this isn\'t nor is this'; return test(text, result, done, {stream: true}); }); }); describe('with custom colors enabled', function () { it('renders basic colors', function (done) { const text = ['\x1b[31mblue', 'not blue']; const result = 'bluenot blue'; return test(text, result, done, {colors: {1: '#00A'}}); }); it('renders basic colors with streaming', function (done) { const text = ['\x1b[31mblue', 'also blue']; const result = 'bluealso blue'; return test(text, result, done, {stream: true, colors: {1: '#00A'}}); }); it('renders custom colors and default colors', function (done) { const text = ['\x1b[31mblue', 'not blue', '\x1b[94mlight blue', 'not colored']; const result = 'bluenot bluelight bluenot colored'; return test(text, result, done, {colors: {1: '#00A'}}); }); it('renders custom colors and default colors together', function (done) { const text = ['\x1b[31mblue', 'not blue', '\x1b[94mlight blue', 'not colored']; const result = 'bluenot bluelight bluenot colored'; return test(text, result, done, {colors: {1: '#00A'}}); }); it('renders custom 8/ 16 colors', function (done) { // code - 90 + 8 = color // so 94 - 90 + 8 = 12 const text = ['\x1b[94mlighter blue']; const result = 'lighter blue'; return test(text, result, done, {colors: {12: '#33F'}}); }); it('renders custom 256 colors', function (done) { // code - 90 + 8 = color // so 94 - 90 + 8 = 12 const text = ['\x1b[38;5;125mdark red', 'then \x1b[38;5;126msome other color']; const result = 'dark redthen some other color'; return test(text, result, done, {colors: {126: '#af225f'}}); }); }); });