import chalk from "chalk"; import stripAnsi from "strip-ansi"; import codeFrame, { codeFrameColumns } from ".."; describe("@babel/code-frame", function() { test("basic usage", function() { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect(codeFrame(rawLines, 2, 16)).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^", " 3 | };", ].join("\n"), ); }); test("optional column number", function() { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect(codeFrame(rawLines, 2, null)).toEqual( [" 1 | class Foo {", "> 2 | constructor()", " 3 | };"].join("\n"), ); }); test("maximum context lines and padding", function() { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2)).toEqual( [ " 5 | * @param b Number", " 6 | * @returns Number", "> 7 | */", " | ^", " 8 | ", " 9 | function sum(a, b) {", " 10 | return a + b", ].join("\n"), ); }); test("no unnecessary padding due to one-off errors", function() { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 6, 2)).toEqual( [ " 4 | * @param a Number", " 5 | * @param b Number", "> 6 | * @returns Number", " | ^", " 7 | */", " 8 | ", " 9 | function sum(a, b) {", ].join("\n"), ); }); test("tabs", function() { const rawLines = [ "\tclass Foo {", "\t \t\t constructor\t(\t)", "\t};", ].join("\n"); expect(codeFrame(rawLines, 2, 25)).toEqual( [ " 1 | \tclass Foo {", "> 2 | \t \t\t constructor\t(\t)", " | \t \t\t \t \t ^", " 3 | \t};", ].join("\n"), ); }); test("opts.highlightCode", function() { const rawLines = "console.log('babel')"; const result = codeFrame(rawLines, 1, 9, { highlightCode: true }); const stripped = stripAnsi(result); expect(result.length).toBeGreaterThan(stripped.length); expect(stripped).toEqual( ["> 1 | console.log('babel')", " | ^"].join("\n"), ); }); test("opts.linesAbove", function() { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2, { linesAbove: 1 })).toEqual( [ " 6 | * @returns Number", "> 7 | */", " | ^", " 8 | ", " 9 | function sum(a, b) {", " 10 | return a + b", ].join("\n"), ); }); test("opts.linesBelow", function() { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2, { linesBelow: 1 })).toEqual( [ " 5 | * @param b Number", " 6 | * @returns Number", "> 7 | */", " | ^", " 8 | ", ].join("\n"), ); }); test("opts.linesAbove and opts.linesBelow", function() { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2, { linesAbove: 1, linesBelow: 1 })).toEqual( [" 6 | * @returns Number", "> 7 | */", " | ^", " 8 | "].join( "\n", ), ); }); test("opts.linesAbove no lines above", function() { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2 } }, { linesAbove: 0 }), ).toEqual( [ "> 2 | constructor() {", " 3 | console.log(arguments);", " 4 | }", " 5 | };", ].join("\n"), ); }); test("opts.linesBelow no lines below", function() { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2 } }, { linesBelow: 0 }), ).toEqual([" 1 | class Foo {", "> 2 | constructor() {"].join("\n")); }); test("opts.linesBelow single line", function() { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2 } }, { linesAbove: 0, linesBelow: 0 }, ), ).toEqual(["> 2 | constructor() {"].join("\n")); }); test("opts.forceColor", function() { const marker = chalk.red.bold; const gutter = chalk.grey; const rawLines = ["", "", "", ""].join("\n"); expect( codeFrame(rawLines, 3, null, { linesAbove: 1, linesBelow: 1, forceColor: true, }), ).toEqual( chalk.reset( [ " " + gutter(" 2 | "), marker(">") + gutter(" 3 | "), " " + gutter(" 4 | "), ].join("\n"), ), ); }); test("basic usage, new API", function() { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 16 } }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^", " 3 | };", ].join("\n"), ); }); test("mark multiple columns", function() { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 3 }, end: { line: 2, column: 16 }, }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^^^^^^^^^^^^^", " 3 | };", ].join("\n"), ); }); test("mark multiple columns across lines", function() { const rawLines = ["class Foo {", " constructor() {", " }", "};"].join( "\n", ); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 17 }, end: { line: 3, column: 3 }, }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", " | ^", "> 3 | }", " | ^^^", " 4 | };", ].join("\n"), ); }); test("mark multiple columns across multiple lines", function() { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 17 }, end: { line: 4, column: 3 }, }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", " | ^", "> 3 | console.log(arguments);", " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^", "> 4 | }", " | ^^^", " 5 | };", ].join("\n"), ); }); test("mark across multiple lines without columns", function() { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2 }, end: { line: 4 } }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", "> 3 | console.log(arguments);", "> 4 | }", " 5 | };", ].join("\n"), ); }); test("opts.message", function() { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2, column: 16 } }, { message: "Missing {", }, ), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^ Missing {", " 3 | };", ].join("\n"), ); }); test("opts.message without column", function() { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2 } }, { message: "Missing {", }, ), ).toEqual( [ " Missing {", " 1 | class Foo {", "> 2 | constructor()", " 3 | };", ].join("\n"), ); }); test("opts.message with multiple lines", function() { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2, column: 17 }, end: { line: 4, column: 3 }, }, { message: "something about the constructor body", }, ), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", " | ^", "> 3 | console.log(arguments);", " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^", "> 4 | }", " | ^^^ something about the constructor body", " 5 | };", ].join("\n"), ); }); test("opts.message with multiple lines without columns", function() { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2 }, end: { line: 4 } }, { message: "something about the constructor body", }, ), ).toEqual( [ " something about the constructor body", " 1 | class Foo {", "> 2 | constructor() {", "> 3 | console.log(arguments);", "> 4 | }", " 5 | };", ].join("\n"), ); }); });