import assert from 'assert'; import {readFileSync} from 'fs'; import {join as joinPath} from 'path'; import codeframe from '../src/codeframe'; const LINE_END = '\n'; describe('codeframe', () => { it('should create a codeframe', () => { let codeframeString = codeframe( 'hello world', [ { start: { column: 1, line: 1, }, end: { column: 1, line: 1, }, }, { start: { column: 3, line: 1, }, end: { column: 5, line: 1, }, }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^ ^^^'); }); it('should create a codeframe with multiple lines', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe', [ { start: { column: 1, line: 1, }, end: { column: 1, line: 1, }, }, { start: { column: 7, line: 1, }, end: { column: 10, line: 2, }, }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^ ^^^^^'); assert.equal(lines[2], '> 2 | Enjoy this nice codeframe'); assert.equal(lines[3], '> | ^^^^^^^^^^'); }); it('should handle unordered overlapping highlights properly', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe', [ { start: { column: 1, line: 1, }, end: { column: 1, line: 1, }, }, { start: { column: 7, line: 1, }, end: { column: 10, line: 2, }, }, { start: { column: 4, line: 2, }, end: { column: 7, line: 2, }, }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^ ^^^^^'); assert.equal(lines[2], '> 2 | Enjoy this nice codeframe'); assert.equal(lines[3], '> | ^^^^^^^^^^'); }); it('should handle partial overlapping highlights properly', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe', [ { start: { column: 1, line: 1, }, end: { column: 1, line: 1, }, }, { start: { column: 7, line: 1, }, end: { column: 10, line: 2, }, }, { start: { column: 4, line: 2, }, end: { column: 12, line: 2, }, }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^ ^^^^^'); assert.equal(lines[2], '> 2 | Enjoy this nice codeframe'); assert.equal(lines[3], '> | ^^^^^^^^^^^^'); }); it('should be able to render inline messages', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe', [ { start: { column: 1, line: 1, }, end: { column: 6, line: 1, }, message: 'test', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^^^^^^ test'); assert.equal(lines[2], ' 2 | Enjoy this nice codeframe'); }); it('should only render last inline message of a column', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe', [ { start: { column: 1, line: 1, }, end: { column: 3, line: 1, }, message: 'test', }, { start: { column: 1, line: 1, }, end: { column: 6, line: 1, }, message: 'this should be printed', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^^^^^^ this should be printed'); assert.equal(lines[2], ' 2 | Enjoy this nice codeframe'); }); it('should only render last inline message of a column with space', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe', [ { start: { column: 1, line: 1, }, end: { column: 1, line: 1, }, message: 'test', }, { start: { column: 3, line: 1, }, end: { column: 7, line: 1, }, message: 'this should be printed', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^ ^^^^^ this should be printed'); assert.equal(lines[2], ' 2 | Enjoy this nice codeframe'); }); it('should only render last inline message of a column with multiple lines and space', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe\nThis is another line', [ { start: { column: 1, line: 1, }, end: { column: 1, line: 1, }, message: 'test', }, { start: { column: 3, line: 1, }, end: { column: 7, line: 1, }, message: 'this should be printed', }, { start: { column: 3, line: 2, }, end: { column: 7, line: 3, }, message: 'message line 2', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^ ^^^^^ this should be printed'); assert.equal(lines[2], '> 2 | Enjoy this nice codeframe'); assert.equal(lines[3], '> | ^^^^^^^^^^^^^^^^^^^^^^^'); assert.equal(lines[4], '> 3 | This is another line'); assert.equal(lines[5], '> | ^^^^^^^ message line 2'); }); it('should only render last inline message of a column with multiple lines and space', () => { let codeframeString = codeframe( 'hello world\nEnjoy this nice codeframe\nThis is another line', [ { start: { column: 1, line: 1, }, end: { column: 1, line: 1, }, message: 'test', }, { start: { column: 3, line: 1, }, end: { column: 7, line: 1, }, message: 'this should be printed', }, { start: { column: 3, line: 2, }, end: { column: 7, line: 3, }, message: 'message line 2', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^ ^^^^^ this should be printed'); assert.equal(lines[2], '> 2 | Enjoy this nice codeframe'); assert.equal(lines[3], '> | ^^^^^^^^^^^^^^^^^^^^^^^'); assert.equal(lines[4], '> 3 | This is another line'); assert.equal(lines[5], '> | ^^^^^^^ message line 2'); }); it('should properly use padding', () => { let codeframeString = codeframe( 'test\n'.repeat(100), [ { start: { column: 2, line: 5, }, end: { column: 2, line: 5, }, message: 'test', }, ], { useColor: false, padding: { before: 2, after: 4, }, }, ); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 8); assert.equal(lines[0], ' 3 | test'); assert.equal(lines[2], '> 5 | test'); assert.equal(lines[3], '> | ^ test'); assert.equal(lines[7], ' 9 | test'); }); it('should properly pad numbers for large files', () => { let codeframeString = codeframe('test\n'.repeat(1000), [ { start: { column: 2, line: 99, }, end: { column: 2, line: 99, }, message: 'test', }, { start: { column: 2, line: 100, }, end: { column: 2, line: 100, }, message: 'test 2', }, ]); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 7); assert.equal(lines[0], ' 98 | test'); assert.equal(lines[1], '> 99 | test'); assert.equal(lines[2], '> | ^ test'); assert.equal(lines[3], '> 100 | test'); assert.equal(lines[4], '> | ^ test 2'); assert.equal(lines[5], ' 101 | test'); assert.equal(lines[6], ' 102 | test'); }); it('should properly pad numbers for short files', () => { let codeframeString = codeframe('test\n'.repeat(1000), [ { start: { column: 2, line: 7, }, end: { column: 2, line: 7, }, message: 'test', }, { start: { column: 2, line: 12, }, end: { column: 2, line: 12, }, message: 'test', }, ]); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 11); assert.equal(lines[0], ' 6 | test'); assert.equal(lines[4], ' 9 | test'); assert.equal(lines[5], ' 10 | test'); assert.equal(lines[6], ' 11 | test'); assert.equal(lines[10], ' 14 | test'); }); it('should properly use maxLines', () => { let line = 'test '.repeat(100); let codeframeString = codeframe( `${line}\n`.repeat(100), [ { start: { column: 2, line: 5, }, end: { column: 2, line: 5, }, message: 'test', }, { start: { column: 2, line: 12, }, end: { column: 2, line: 20, }, message: 'test', }, ], { useColor: false, maxLines: 10, terminalWidth: 5, }, ); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 13); assert.equal(lines[0], ' 4 | test test '); assert.equal(lines[7], ' 10 | test test '); assert.equal(lines[11], '> 13 | test test '); assert.equal(lines[12], '> | ^^^^^^^^^^'); }); it('should be able to handle tabs', () => { let codeframeString = codeframe( 'hel\tlo wor\tld\nEnjoy thi\ts nice cod\teframe', [ { start: { column: 5, line: 1, }, end: { column: 8, line: 1, }, message: 'test', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hel lo wor ld'); assert.equal(lines[1], '> | ^^^^ test'); assert.equal(lines[2], ' 2 | Enjoy thi s nice cod eframe'); }); it('should be able to handle tabs with multiple highlights', () => { let codeframeString = codeframe( 'hel\tlo wor\tld\nEnjoy thi\ts nice cod\teframe', [ { start: { column: 3, line: 1, }, end: { column: 5, line: 1, }, message: 'test', }, { start: { column: 7, line: 1, }, end: { column: 8, line: 1, }, message: 'test', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hel lo wor ld'); assert.equal(lines[1], '> | ^^^^ ^^ test'); assert.equal(lines[2], ' 2 | Enjoy thi s nice cod eframe'); }); it('multiline highlights with tabs', () => { let codeframeString = codeframe( 'hel\tlo wor\tld\nEnjoy thi\ts nice cod\teframe\ntest', [ { start: { column: 3, line: 1, }, end: { column: 2, line: 3, }, message: 'test', }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hel lo wor ld'); assert.equal(lines[1], '> | ^^^^^^^^^^^^^'); assert.equal(lines[2], '> 2 | Enjoy thi s nice cod eframe'); assert.equal(lines[3], '> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'); assert.equal(lines[4], '> 3 | test'); assert.equal(lines[5], '> | ^^ test'); }); it('Should truncate long lines and print message', () => { let originalLine = 'hello world '.repeat(1000); let codeframeString = codeframe( originalLine, [ { start: { column: 1000, line: 1, }, end: { column: 1200, line: 1, }, message: 'This is a message', }, ], {useColor: false, terminalWidth: 25}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 2); assert.equal(lines[0], '> 1 | d hello world hello'); assert.equal(lines[1], '> | ^^^^^^^^^^^^^^ This is a message'); }); it('Truncation across multiple lines', () => { let originalLine = 'hello world '.repeat(100) + '\n' + 'new line '.repeat(100); let codeframeString = codeframe( originalLine, [ { start: { column: 15, line: 1, }, end: { column: 400, line: 1, }, message: 'This is the first line', }, { start: { column: 2, line: 2, }, end: { column: 100, line: 2, }, message: 'This is the second line', }, ], {useColor: false, terminalWidth: 25}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 4); assert.equal(lines[0], '> 1 | ld hello world hell'); assert.equal(lines[1], '> | ^^^^^^^^^^^^^^ This is the first line'); assert.equal(lines[2], '> 2 | new line new line n'); assert.equal(lines[3], '> | ^^^^^^^^^^^^^^^^^^ This is the second line'); }); it('Truncation across various types and positions of highlights', () => { let originalLine = 'hello world '.repeat(100) + '\n' + 'new line '.repeat(100); let codeframeString = codeframe( originalLine, [ { start: { column: 2, line: 1, }, end: { column: 5, line: 1, }, }, { start: { column: 6, line: 1, }, end: { column: 10, line: 1, }, message: 'I have a message', }, { start: { column: 15, line: 1, }, end: { column: 25, line: 1, }, message: 'I also have a message', }, { start: { column: 2, line: 2, }, end: { column: 5, line: 2, }, message: 'This is the second line', }, ], {useColor: false, terminalWidth: 25}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 4); assert.equal(lines[0], '> 1 | hello world hello w'); assert.equal(lines[1], '> | ^^^^^^^^^ ^^^^^ I also have a message'); assert.equal(lines[2], '> 2 | new line new line n'); assert.equal(lines[3], '> | ^^^^ This is the second line'); }); it('Multi-line highlight w/ truncation', () => { let originalLine = 'hello world '.repeat(100) + '\n' + 'new line '.repeat(100); let codeframeString = codeframe( originalLine, [ { start: { column: 2, line: 1, }, end: { column: 151, line: 2, }, message: 'I have a message', }, ], {useColor: false, terminalWidth: 25}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 4); assert.equal(lines[0], '> 1 | hello world hello w'); assert.equal(lines[1], '> | ^^^^^^^^^^^^^^^^^^'); assert.equal(lines[2], '> 2 | ew line new line ne'); assert.equal(lines[3], '> | ^^^^^^ I have a message'); }); it('Should pad properly, T-650', () => { let fileContent = readFileSync( joinPath(__dirname, './fixtures/a.js'), 'utf8', ); let codeframeString = codeframe( fileContent, [ { start: { line: 8, column: 10, }, end: { line: 8, column: 48, }, }, ], { useColor: false, syntaxHighlighting: false, language: 'js', terminalWidth: 100, }, ); let lines = codeframeString.split(LINE_END); assert.equal(lines.length, 5); assert.equal(lines[0], ` 7 | import Tooltip from '../tooltip';`); assert.equal( lines[1], `> 8 | import VisuallyHidden from '../visually-hidden';`, ); assert.equal( lines[2], '> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', ); assert.equal(lines[3], ' 9 | '); assert.equal(lines[4], ' 10 | /**'); }); it('should still generate a codeframe when end is before start', () => { let codeframeString = codeframe( 'hello world', [ { start: { column: 5, line: 1, }, end: { column: 1, line: 1, }, }, ], {useColor: false}, ); let lines = codeframeString.split(LINE_END); assert.equal(lines[0], '> 1 | hello world'); assert.equal(lines[1], '> | ^'); }); });