package syntax import "testing" // wantJS is wantLine for JavaScript. func wantJS(t *testing.T, src string, line int, want ...string) { t.Helper() wantLine(t, LanguageJavaScript, src, line, want...) } func TestJavaScriptColoursKeywords(t *testing.T) { wantJS(t, "const x = 1", 0, "const:keyword", "x:identifier", "=:operator", "1:number") wantJS(t, "async function f() {}", 0, "async:keyword", "function:keyword", "f:function", "(:punctuation", "):punctuation", "{:punctuation", "}:punctuation") } func TestJavaScriptColoursConstantsAndGlobals(t *testing.T) { wantJS(t, "x = true", 0, "x:identifier", "=:operator", "true:constant") wantJS(t, "x = null", 0, "x:identifier", "=:operator", "null:constant") wantJS(t, "console.log(1)", 0, "console:builtin", ".:punctuation", "log:function", "(:punctuation", "1:number", "):punctuation") } func TestANameBeforeAParenthesisIsACall(t *testing.T) { wantJS(t, "doThing()", 0, "doThing:function", "(:punctuation", "):punctuation") wantJS(t, "doThing", 0, "doThing:identifier") } func TestJavaScriptColoursStrings(t *testing.T) { wantJS(t, `x = "double"`, 0, "x:identifier", "=:operator", `"double":string`) wantJS(t, "x = 'single'", 0, "x:identifier", "=:operator", "'single':string") } func TestAnEscapedQuoteDoesNotEndAJavaScriptString(t *testing.T) { wantJS(t, `x = "he said \"no\""`, 0, "x:identifier", "=:operator", `"he said \"no\"":string`) } func TestAnUnterminatedJavaScriptStringIsColouredToTheEndOfTheLine(t *testing.T) { wantJS(t, `x = "half written`, 0, "x:identifier", "=:operator", `"half written:string`) } func TestJavaScriptColoursLineAndBlockComments(t *testing.T) { wantJS(t, "x = 1 // a note", 0, "x:identifier", "=:operator", "1:number", "// a note:comment") wantJS(t, "x = /* inline */ 1", 0, "x:identifier", "=:operator", "/* inline */:comment", "1:number") } func TestABlockCommentCarriesAcrossLines(t *testing.T) { src := "/* first\n second\n third */ x = 1" wantJS(t, src, 0, "/* first:comment") wantJS(t, src, 1, " second:comment") wantJS(t, src, 2, " third */:comment", "x:identifier", "=:operator", "1:number") } func TestCodeInsideABlockCommentIsNotColoured(t *testing.T) { src := "/*\nconst x = 1\n*/" wantJS(t, src, 1, "const x = 1:comment") } func TestJavaScriptColoursTemplateLiterals(t *testing.T) { wantJS(t, "x = `plain`", 0, "x:identifier", "=:operator", "`plain`:string") wantJS(t, "x = `a ${b} c`", 0, "x:identifier", "=:operator", "`a ${b} c`:string") } func TestATemplateLiteralCarriesAcrossLines(t *testing.T) { src := "const html = `\n
hi
\n`\nconst x = 1" wantJS(t, src, 1, "hi
:string") wantJS(t, src, 2, "`:string") // The line after it is code again. wantJS(t, src, 3, "const:keyword", "x:identifier", "=:operator", "1:number") } func TestJavaScriptColoursNumbers(t *testing.T) { for _, value := range []string{"42", "3.14", "0x1f", "0b1010", "0o777", "1_000_000", "1e6", "10n"} { t.Run(value, func(t *testing.T) { got := spansOf(t, LanguageJavaScript, "x = "+value, 0) if last := got[len(got)-1]; last != value+":number" { t.Errorf("%q was coloured %q, want a number", value, last) } }) } } func TestJavaScriptTakesOperatorsInRuns(t *testing.T) { wantJS(t, "a === b", 0, "a:identifier", "===:operator", "b:identifier") wantJS(t, "a ?? b", 0, "a:identifier", "??:operator", "b:identifier") wantJS(t, "a => b", 0, "a:identifier", "=>:operator", "b:identifier") } func TestARegexIsLeftAsOperatorsRatherThanGuessedAt(t *testing.T) { // Telling /x/g from a division needs to know whether the previous token // could end an expression. Getting it wrong colours the rest of the line // as a string, which is worse than this. got := spansOf(t, LanguageJavaScript, "x = /ab/g", 0) for _, span := range got { if len(span) > 7 && span[len(span)-7:] == ":string" { t.Errorf("a regex was guessed at as a string: %v", got) } } } func TestJavaScriptGivesOneEntryPerLine(t *testing.T) { if got := len(Highlight(LanguageJavaScript, "a\nb\n\nc\n")); got != 5 { t.Errorf("Highlight() returned %d lines for a 5-line document", got) } } func TestJavaScriptSpansStayInsideTheirLine(t *testing.T) { src := "import { a } from 'b'\n/* c\nd */\nconst t = `x${y}z`\nconsole.log(0x1f)\n" assertSpansFit(t, LanguageJavaScript, src) }