// Copyright (c) 2024-2025 David Vogel // // This software is released under the MIT License. // https://opensource.org/licenses/MIT package typst_test import ( "bytes" "errors" "testing" "github.com/Dadido3/go-typst" "github.com/google/go-cmp/cmp" ) func TestErrors0(t *testing.T) { cli := typst.CLI{} r := bytes.NewBufferString(`This is a test!`) var w bytes.Buffer if err := cli.Compile(r, &w, nil); err != nil { t.Fatalf("Failed to compile document: %v", err) } } func TestErrors1(t *testing.T) { cli := typst.CLI{} r := bytes.NewBufferString(`This is a test! #assert(1 < 1, message: "Test")`) var w bytes.Buffer if err := cli.Compile(r, &w, nil); err == nil { t.Fatalf("Expected error, but got nil") } else { var errTypst *typst.Error if errors.As(err, &errTypst) { if len(errTypst.Details) != 1 { t.Fatalf("Expected error doesn't contain the expected number of detail entries. Got %v, want %v", len(errTypst.Details), 1) } details := errTypst.Details[0] if details.Message != "error: assertion failed: Test" { t.Errorf("Expected error with error message %q, got %q", "error: assertion failed: Test", details.Message) } /*if details.Path != "" { t.Errorf("Expected error to point to path %q, got path %q", "", details.Path) }*/ if details.Line != 3 { t.Errorf("Expected error to point at line %d, got line %d", 3, details.Line) } if details.Column != 1 { t.Errorf("Expected error to point at column %d, got column %d", 1, details.Column) } } else { t.Errorf("Expected error type %T, got %T: %v", errTypst, err, err) } } } func TestErrors2(t *testing.T) { cli := typst.CLI{} opts := typst.CLIOptions{ Pages: "a", } r := bytes.NewBufferString(`This is a test!`) var w bytes.Buffer if err := cli.Compile(r, &w, &opts); err == nil { t.Fatalf("Expected error, but got nil") } else { var errTypst *typst.Error if errors.As(err, &errTypst) { if len(errTypst.Details) != 1 { t.Fatalf("Expected error doesn't contain the expected number of detail entries. Got %v, want %v", len(errTypst.Details), 1) } details := errTypst.Details[0] // Don't check the specific error message, as that may change over time. // The expected message should be similar to: error: invalid value 'a' for '--pages ': not a valid page number. if details.Message == "" { t.Errorf("Expected error message, got %q", details.Message) } } else { t.Errorf("Expected error type %T, got %T: %v", errTypst, err, err) } } } func TestErrorParsing(t *testing.T) { var tests = map[string]struct { StdErr string // The original and raw stderr message. ExpectedDetails []typst.ErrorDetails // Expected parsed result. }{ "Typst 0.13.0 HTML warning + error": { StdErr: "warning: html export is under active development and incomplete\n = hint: its behaviour may change at any time\n = hint: do not rely on this feature for production use cases\n = hint: see https://github.com/typst/typst/issues/5512 for more information\n\nerror: page configuration is not allowed inside of containers\n ┌─ \\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\:1:1\n │\n1 │ #set page(width: 100mm, height: auto, margin: 5mm)\n │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n", ExpectedDetails: []typst.ErrorDetails{ { Message: "warning: html export is under active development and incomplete\n = hint: its behaviour may change at any time\n = hint: do not rely on this feature for production use cases\n = hint: see https://github.com/typst/typst/issues/5512 for more information", }, { Message: "error: page configuration is not allowed inside of containers", Path: "\\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\", Line: 1, Column: 1, }, }, }, "Typst 0.13.0 error with path": { StdErr: "error: expected expression\n ┌─ \\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\:12:34\n │\n12 │ - Test coverage of most features.#\n │ ^\n\n", ExpectedDetails: []typst.ErrorDetails{ { Message: "error: expected expression", Path: "\\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\", Line: 12, Column: 34, }, }, }, "Typst 0.13.0 multiple errors with paths": { StdErr: "error: expected expression\n ┌─ \\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\:11:53\n │\n11 │ - Uses stdio; No temporary files need to be created.#\n │ ^\n\nerror: expected expression\n ┌─ \\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\:12:34\n │\n12 │ - Test coverage of most features.#\n │ ^\n\n", ExpectedDetails: []typst.ErrorDetails{ { Message: "error: expected expression", Path: "\\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\", Line: 11, Column: 53, }, { Message: "error: expected expression", Path: "\\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\", Line: 12, Column: 34, }, }, }, "Typst 0.13.0 stacked errors with paths": { StdErr: "error: expected expression\n ┌─ \\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\test.typ:1:4\n │\n1 │ hey#\n │ ^\n\nhelp: error occurred while importing this module\n ┌─ \\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\:14:9\n │\n14 │ #include \"test.typ\"\n │ ^^^^^^^^^^\n\n", ExpectedDetails: []typst.ErrorDetails{ { Message: "error: expected expression", Path: "\\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\test.typ", Line: 1, Column: 4, }, { Message: "help: error occurred while importing this module", Path: "\\\\?\\C:\\Users\\David Vogel\\Desktop\\Synced\\Go\\Libraries\\go-typst\\", Line: 14, Column: 9, }, }, }, "Typst 0.13.0 error without path": { StdErr: "error: invalid value 'a' for '--pages ': not a valid page number\n\nFor more information, try '--help'.\n", ExpectedDetails: []typst.ErrorDetails{ { Message: "error: invalid value 'a' for '--pages ': not a valid page number", }, }, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { result := typst.ParseStderr(tt.StdErr, nil) var typstError *typst.Error if errors.As(result, &typstError) { if !cmp.Equal(typstError.Details, tt.ExpectedDetails) { t.Errorf("Parsed details don't match expected details: %s", cmp.Diff(tt.ExpectedDetails, typstError.Details)) } } else { t.Errorf("Parsed error is not of type %T", typstError) } }) } }