d2/e2etests/e2e_test.go

215 lines
4.7 KiB
Go
Raw Normal View History

package e2etests
import (
"context"
2022-11-12 21:31:08 +00:00
"encoding/xml"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"cdr.dev/slog"
trequire "github.com/stretchr/testify/require"
2022-12-01 18:22:55 +00:00
"oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/util-go/diff"
2022-12-27 04:51:37 +00:00
"oss.terrastruct.com/d2/d2compiler"
2022-11-11 19:43:56 +00:00
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
2022-11-11 19:43:56 +00:00
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
2022-12-27 04:51:37 +00:00
"oss.terrastruct.com/d2/d2layouts/d2near"
"oss.terrastruct.com/d2/d2layouts/d2sequence"
"oss.terrastruct.com/d2/d2lib"
"oss.terrastruct.com/d2/d2renderers/d2svg"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/log"
"oss.terrastruct.com/d2/lib/textmeasure"
)
func TestE2E(t *testing.T) {
t.Parallel()
2022-11-11 19:43:56 +00:00
t.Run("sanity", testSanity)
t.Run("stable", testStable)
t.Run("regression", testRegression)
t.Run("todo", testTodo)
t.Run("measured", testMeasured)
2023-02-14 06:28:32 +00:00
t.Run("unicode", testUnicode)
}
2022-11-11 19:43:56 +00:00
func testSanity(t *testing.T) {
tcs := []testCase{
2022-12-05 17:48:38 +00:00
{
name: "empty",
script: ``,
},
2022-11-11 19:43:56 +00:00
{
name: "basic",
script: `a -> b
`,
},
{
name: "1 to 2",
script: `a -> b
a -> c
`,
},
{
name: "child to child",
script: `a.b -> c.d
`,
},
{
name: "connection label",
script: `a -> b: hello
`,
},
}
runa(t, tcs)
}
type testCase struct {
name string
script string
mtexts []*d2target.MText
assertions func(t *testing.T, diagram *d2target.Diagram)
skip bool
2023-02-08 04:47:12 +00:00
expErr string
}
func runa(t *testing.T, tcs []testCase) {
for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
if tc.skip {
t.Skip()
}
t.Parallel()
run(t, tc)
})
}
}
2022-12-27 04:51:37 +00:00
// serde exercises serializing and deserializing the graph
2022-12-27 04:52:47 +00:00
// We want to run all the steps leading up to serialization in the course of regular layout
2022-12-27 04:51:37 +00:00
func serde(t *testing.T, tc testCase, ruler *textmeasure.Ruler) {
ctx := context.Background()
ctx = log.WithTB(ctx, t, nil)
g, err := d2compiler.Compile("", strings.NewReader(tc.script), &d2compiler.CompileOptions{
UTF16: false,
})
trequire.Nil(t, err)
2022-12-27 04:51:37 +00:00
if len(g.Objects) > 0 {
err = g.SetDimensions(nil, ruler, nil)
trequire.Nil(t, err)
2022-12-27 04:51:37 +00:00
d2near.WithoutConstantNears(ctx, g)
d2sequence.WithoutSequenceDiagrams(ctx, g)
}
b, err := d2graph.SerializeGraph(g)
trequire.Nil(t, err)
2022-12-27 04:51:37 +00:00
var newG d2graph.Graph
err = d2graph.DeserializeGraph(b, &newG)
trequire.Nil(t, err)
2023-02-18 16:56:17 +00:00
trequire.Nil(t, d2graph.CompareSerializedGraph(g, &newG))
2022-12-27 04:51:37 +00:00
}
func run(t *testing.T, tc testCase) {
ctx := context.Background()
ctx = log.WithTB(ctx, t, nil)
ctx = log.Leveled(ctx, slog.LevelDebug)
var ruler *textmeasure.Ruler
var err error
if tc.mtexts == nil {
ruler, err = textmeasure.NewRuler()
trequire.Nil(t, err)
serde(t, tc, ruler)
}
2022-12-27 04:51:37 +00:00
2022-11-11 19:43:56 +00:00
layoutsTested := []string{"dagre", "elk"}
2022-11-11 19:43:56 +00:00
for _, layoutName := range layoutsTested {
var layout func(context.Context, *d2graph.Graph) error
if layoutName == "dagre" {
2022-12-30 21:36:49 +00:00
layout = d2dagrelayout.DefaultLayout
2022-11-11 19:43:56 +00:00
} else if layoutName == "elk" {
// If measured texts exists, we are specifically exercising text measurements, no need to run on both layouts
if tc.mtexts != nil {
continue
}
2022-12-30 21:36:49 +00:00
layout = d2elklayout.DefaultLayout
2022-11-11 19:43:56 +00:00
}
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
Ruler: ruler,
MeasuredTexts: tc.mtexts,
Layout: layout,
})
2023-02-08 04:47:12 +00:00
if tc.expErr != "" {
assert.Error(t, err)
assert.ErrorString(t, err, tc.expErr)
2022-11-11 19:43:56 +00:00
return
2023-02-08 04:47:12 +00:00
} else {
assert.Success(t, err)
2022-11-11 19:43:56 +00:00
}
2022-11-11 19:43:56 +00:00
if tc.assertions != nil {
t.Run("assertions", func(t *testing.T) {
tc.assertions(t, diagram)
})
}
2022-11-11 19:43:56 +00:00
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestE2E/"), layoutName)
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
2022-12-01 18:22:55 +00:00
2022-12-21 07:43:45 +00:00
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
2023-01-27 21:30:44 +00:00
Pad: d2svg.DEFAULT_PADDING,
ThemeID: 0,
2023-01-30 11:06:54 +00:00
DarkThemeID: -1,
2022-12-21 07:43:45 +00:00
})
2022-12-01 18:22:55 +00:00
assert.Success(t, err)
2022-12-02 21:56:13 +00:00
err = os.MkdirAll(dataPath, 0755)
assert.Success(t, err)
2022-12-01 18:22:55 +00:00
err = ioutil.WriteFile(pathGotSVG, svgBytes, 0600)
assert.Success(t, err)
2022-11-11 19:43:56 +00:00
2022-12-24 20:45:12 +00:00
// Check that it's valid SVG
2022-11-12 21:31:08 +00:00
var xmlParsed interface{}
2022-12-01 18:22:55 +00:00
err = xml.Unmarshal(svgBytes, &xmlParsed)
assert.Success(t, err)
2022-11-12 21:31:08 +00:00
2023-02-02 18:48:28 +00:00
var err2 error
2022-12-01 18:22:55 +00:00
err = diff.TestdataJSON(filepath.Join(dataPath, "board"), diagram)
2022-11-11 19:43:56 +00:00
if os.Getenv("SKIP_SVG_CHECK") == "" {
2023-02-02 18:48:28 +00:00
err2 = diff.Testdata(filepath.Join(dataPath, "sketch"), ".svg", svgBytes)
}
2023-02-02 18:48:28 +00:00
assert.Success(t, err)
assert.Success(t, err2)
}
}
func getShape(t *testing.T, diagram *d2target.Diagram, id string) d2target.Shape {
for _, shape := range diagram.Shapes {
if shape.ID == id {
return shape
}
}
t.Fatalf(`Shape "%s" not found`, id)
return d2target.Shape{}
}
func mdTestScript(md string) string {
return fmt.Sprintf(`
md: |md
%s
|
a -> md -> b
`, md)
}