From 20c26041c129c4d1bafe44cba101ed2a65211ffd Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Sat, 18 Nov 2023 21:45:19 -0500 Subject: [PATCH] add some go tests for message formatting --- pkg/api/api_test.go | 321 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 pkg/api/api_test.go diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go new file mode 100644 index 00000000000..c0e36aa6165 --- /dev/null +++ b/pkg/api/api_test.go @@ -0,0 +1,321 @@ +package api_test + +import ( + "testing" + + "github.com/evanw/esbuild/internal/test" + "github.com/evanw/esbuild/pkg/api" +) + +func TestFormatMessages(t *testing.T) { + check := func(name string, opts api.FormatMessagesOptions, msg api.Message, expected string) { + t.Helper() + t.Run(name, func(t *testing.T) { + test.AssertEqualWithDiff(t, api.FormatMessages([]api.Message{msg}, opts)[0], expected) + }) + } + + check("Error", api.FormatMessagesOptions{Kind: api.ErrorMessage}, api.Message{Text: "This is a test"}, "✘ [ERROR] This is a test\n\n") + check("Warning", api.FormatMessagesOptions{Kind: api.WarningMessage}, api.Message{Text: "This is a test"}, "▲ [WARNING] This is a test\n\n") + + check("Basic location", + api.FormatMessagesOptions{}, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 5, // 0-based + Length: 3, + LineText: "this.foo();", + Suggestion: "bar", + }}, + `✘ [ERROR] This is a test + + some file.js:100:5: + 100 │ this.foo(); + │ ~~~ + ╵ bar + +`, + ) + + check("Unicode location", + api.FormatMessagesOptions{ + Kind: api.WarningMessage, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 17, // In UTF-8 bytes + Length: 10, // In UTF-8 bytes + LineText: "𝓉𝒽𝒾𝓈.𝒻ℴℴ();", + Suggestion: "𝒷𝒶𝓇", + }}, + `▲ [WARNING] This is a test + + some file.js:100:17: + 100 │ 𝓉𝒽𝒾𝓈.𝒻ℴℴ(); + │ ~~~ + ╵ 𝒷𝒶𝓇 + +`, + ) + + check("Tab stop rendering", + api.FormatMessagesOptions{ + Kind: api.WarningMessage, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 6, + Length: 4, + LineText: "0\t1\t23\t45\t678", + }}, + `▲ [WARNING] This is a test + + some file.js:100:6: + 100 │ 0 1 23 45 678 + ╵ ~~~~~~ + +`, + ) + + check("Truncated location tail, zero length", + api.FormatMessagesOptions{ + TerminalWidth: 32, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 3, + Length: 0, + LineText: "012345678 abcdefghi ABCDEFGHI 012345678 abcdefghi ABCDEFGHI", + }}, + `✘ [ERROR] This is a test + + some file.js:100:3: + 100 │ 012345678 abcdefg... + ╵ ^ + +`, + ) + + check("Truncated location tail, nonzero length", + api.FormatMessagesOptions{ + TerminalWidth: 32, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 3, + Length: 6, + LineText: "012345678 abcdefghi ABCDEFGHI 012345678 abcdefghi ABCDEFGHI", + }}, + `✘ [ERROR] This is a test + + some file.js:100:3: + 100 │ 012345678 abcdefg... + ╵ ~~~~~~ + +`, + ) + + check("Truncated location tail, truncated length", + api.FormatMessagesOptions{ + TerminalWidth: 32, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 3, + Length: 100, + LineText: "012345678 abcdefghi ABCDEFGHI 012345678 abcdefghi ABCDEFGHI", + }}, + `✘ [ERROR] This is a test + + some file.js:100:3: + 100 │ 012345678 abcdefg... + ╵ ~~~~~~~~~~~~~~ + +`, + ) + + check("Truncated location head, zero length", + api.FormatMessagesOptions{ + TerminalWidth: 32, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 200, + Length: 0, + LineText: "012345678 abcdefghi ABCDEFGHI 012345678 abcdefghi ABCDEFGHI", + }}, + `✘ [ERROR] This is a test + + some file.js:100:59: + 100 │ ...defghi ABCDEFGHI + ╵ ^ + +`, + ) + + check("Truncated location head, nonzero length", + api.FormatMessagesOptions{ + TerminalWidth: 32, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 50, + Length: 200, + LineText: "012345678 abcdefghi ABCDEFGHI 012345678 abcdefghi ABCDEFGHI", + }}, + `✘ [ERROR] This is a test + + some file.js:100:50: + 100 │ ...cdefghi ABCDEFGHI + ╵ ~~~~~~~~~ + +`, + ) + + check("Truncated location head and tail, truncated length", + api.FormatMessagesOptions{ + TerminalWidth: 32, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 30, + Length: 30, + LineText: "012345678 abcdefghi ABCDEFGHI 012345678 abcdefghi ABCDEFGHI", + }}, + `✘ [ERROR] This is a test + + some file.js:100:30: + 100 │ ... 012345678 abc... + ╵ ~~~~~~~~~~~~~ + +`, + ) + + check("Truncated location head and tail, non-truncated length", + api.FormatMessagesOptions{ + TerminalWidth: 32, + }, + api.Message{Text: "This is a test", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 30, + Length: 9, + LineText: "012345678 abcdefghi ABCDEFGHI 012345678 abcdefghi ABCDEFGHI", + }}, + `✘ [ERROR] This is a test + + some file.js:100:30: + 100 │ ...HI 012345678 a... + ╵ ~~~~~~~~~ + +`, + ) + + check("Multi-line line text", + api.FormatMessagesOptions{}, + api.Message{Text: "ReferenceError: Cannot access 'foo' before initialization", Location: &api.Location{ + File: "some file.js", + Line: 100, + Column: 2, + LineText: ` foo(); + at ModuleJob.run (node:internal/modules/esm/module_job:185:25) + at async Promise.all (index 0) + at async ESMLoader.import (node:internal/modules/esm/loader:281:24) + at async loadESM (node:internal/process/esm_loader:88:5) + at async handleMainPromise (node:internal/modules/run_main:65:12)`, + }}, + `✘ [ERROR] ReferenceError: Cannot access 'foo' before initialization + + some file.js:100:2: + 100 │ foo(); + ╵ ^ + + at ModuleJob.run (node:internal/modules/esm/module_job:185:25) + at async Promise.all (index 0) + at async ESMLoader.import (node:internal/modules/esm/loader:281:24) + at async loadESM (node:internal/process/esm_loader:88:5) + at async handleMainPromise (node:internal/modules/run_main:65:12) + +`, + ) + + check("Note formatting", + api.FormatMessagesOptions{ + TerminalWidth: 40, + }, + api.Message{ + Text: "Why would you do this?", + Location: &api.Location{ + File: "some file.js", + Line: 1, + Column: 10, + Length: 16, + LineText: "let ten = +([+!+[]]+[+[]]);", + }, + Notes: []api.Note{{ + Text: "This is 1:", + Location: &api.Location{ + File: "some file.js", + Line: 1, + Column: 12, + Length: 7, + LineText: "let ten = +([+!+[]]+[+[]]);", + Suggestion: "'1'", + }, + }, { + Text: "This is 0:", + Location: &api.Location{ + File: "some file.js", + Line: 1, + Column: 20, + Length: 5, + LineText: "let ten = +([+!+[]]+[+[]]);", + Suggestion: "'0'", + }, + }, { + Text: "The number 0 is created by +[], where [] is the empty array and + is the unary plus, " + + "used to convert the right side to a numeric value. The number 1 is formed as +!+[], where " + + "the boolean value true is converted into the numeric value 1 by the prepended plus sign.", + }}, + }, + `✘ [ERROR] Why would you do this? + + some file.js:1:10: + 1 │ let ten = +([+!+[]]+[+[]]); + ╵ ~~~~~~~~~~~~~~~~ + + This is 1: + + some file.js:1:12: + 1 │ let ten = +([+!+[]]+[+[]]); + │ ~~~~~~~ + ╵ '1' + + This is 0: + + some file.js:1:20: + 1 │ let ten = +([+!+[]]+[+[]]); + │ ~~~~~ + ╵ '0' + + The number 0 is created by +[], where + [] is the empty array and + is the + unary plus, used to convert the right + side to a numeric value. The number 1 + is formed as +!+[], where the boolean + value true is converted into the + numeric value 1 by the prepended plus + sign. + +`, + ) +}