diff --git a/ci/release/docker/Dockerfile b/ci/release/docker/Dockerfile
index 33acac831..0d7789a26 100644
--- a/ci/release/docker/Dockerfile
+++ b/ci/release/docker/Dockerfile
@@ -7,8 +7,7 @@ RUN apt-get update && apt-get install -y ca-certificates curl dumb-init sudo
RUN curl -fsSL https://deb.nodesource.com/setup_19.x | bash -s - && \
apt-get install -y nodejs
-# See https://github.com/microsoft/playwright/issues/18319
-RUN npx playwright@1.31.1 install-deps chromium
+RUN npx playwright@1.31.1 install --with-deps chromium
RUN adduser --gecos '' --disabled-password debian \
&& echo "debian ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd
diff --git a/ci/test.sh b/ci/test.sh
index da523b72d..f7d92a6de 100755
--- a/ci/test.sh
+++ b/ci/test.sh
@@ -8,5 +8,6 @@ fi
if [ "${CI:-}" ]; then
export FORCE_COLOR=1
+ npx playwright@1.31.1 install --with-deps chromium
fi
go test --timeout=30m "$@"
diff --git a/d2cli/main.go b/d2cli/main.go
index 11f534569..a96277ce3 100644
--- a/d2cli/main.go
+++ b/d2cli/main.go
@@ -152,6 +152,12 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
outputPath = renameExt(inputPath, ".svg")
}
}
+ inputPath = filepath.Join(ms.PWD, inputPath)
+ d, err := os.Stat(inputPath)
+ if err == nil && d.IsDir() {
+ inputPath = filepath.Join(inputPath, "index.d2")
+ }
+ outputPath = filepath.Join(ms.PWD, outputPath)
match := d2themescatalog.Find(*themeFlag)
if match == (d2themes.Theme{}) {
diff --git a/e2etests-cli/main_test.go b/e2etests-cli/main_test.go
index 22d88dac1..29529b867 100644
--- a/e2etests-cli/main_test.go
+++ b/e2etests-cli/main_test.go
@@ -2,20 +2,125 @@ package e2etests_cli
import (
"context"
+ "os"
+ "path/filepath"
"testing"
"time"
+
+ "oss.terrastruct.com/d2/d2cli"
+ "oss.terrastruct.com/util-go/assert"
+ "oss.terrastruct.com/util-go/diff"
+ "oss.terrastruct.com/util-go/xmain"
+ "oss.terrastruct.com/util-go/xos"
)
func TestCLI_E2E(t *testing.T) {
t.Parallel()
tca := []struct {
- name string
- run func(t *testing.T, ctx context.Context)
+ name string
+ skipCI bool
+ run func(t *testing.T, ctx context.Context, dir string, env *xos.Env)
}{
{
- name: "hello_world",
- run: func(t *testing.T, ctx context.Context) {},
+ name: "hello_world_png",
+ skipCI: true,
+ run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
+ writeFile(t, dir, "hello-world.d2", `x -> y`)
+ err := runTestMain(t, ctx, dir, env, "hello-world.d2", "hello-world.png")
+ assert.Success(t, err)
+ png := readFile(t, dir, "hello-world.png")
+ testdataIgnoreDiff(t, ".png", png)
+ },
+ },
+ {
+ name: "hello_world_png_pad",
+ skipCI: true,
+ run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
+ writeFile(t, dir, "hello-world.d2", `x -> y`)
+ err := runTestMain(t, ctx, dir, env, "--pad=400", "hello-world.d2", "hello-world.png")
+ assert.Success(t, err)
+ png := readFile(t, dir, "hello-world.png")
+ testdataIgnoreDiff(t, ".png", png)
+ },
+ },
+ {
+ name: "hello_world_png_sketch",
+ skipCI: true,
+ run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
+ writeFile(t, dir, "hello-world.d2", `x -> y`)
+ err := runTestMain(t, ctx, dir, env, "--sketch", "hello-world.d2", "hello-world.png")
+ assert.Success(t, err)
+ png := readFile(t, dir, "hello-world.png")
+ // https://github.com/terrastruct/d2/pull/963#pullrequestreview-1323089392
+ testdataIgnoreDiff(t, ".png", png)
+ },
+ },
+ {
+ name: "multiboard/life",
+ run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
+ writeFile(t, dir, "life.d2", `x -> y
+layers: {
+ core: {
+ belief
+ food
+ diet
+ }
+ broker: {
+ mortgage
+ realtor
+ }
+ stocks: {
+ TSX
+ NYSE
+ NASDAQ
+ }
+}
+
+scenarios: {
+ why: {
+ y -> x
+ }
+}
+`)
+ err := runTestMain(t, ctx, dir, env, "life.d2")
+ assert.Success(t, err)
+
+ assert.TestdataDir(t, filepath.Join(dir, "life"))
+ },
+ },
+ {
+ name: "multiboard/life_index_d2",
+ run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
+ writeFile(t, dir, "life/index.d2", `x -> y
+layers: {
+ core: {
+ belief
+ food
+ diet
+ }
+ broker: {
+ mortgage
+ realtor
+ }
+ stocks: {
+ TSX
+ NYSE
+ NASDAQ
+ }
+}
+
+scenarios: {
+ why: {
+ y -> x
+ }
+}
+`)
+ err := runTestMain(t, ctx, dir, env, "life")
+ assert.Success(t, err)
+
+ assert.TestdataDir(t, filepath.Join(dir, "life"))
+ },
},
}
@@ -25,10 +130,74 @@ func TestCLI_E2E(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
+ if tc.skipCI && os.Getenv("CI") != "" {
+ t.SkipNow()
+ }
+
ctx, cancel := context.WithTimeout(ctx, time.Minute*5)
defer cancel()
- tc.run(t, ctx)
+ dir, cleanup := assert.TempDir(t)
+ defer cleanup()
+
+ env := xos.NewEnv(nil)
+
+ tc.run(t, ctx, dir, env)
})
}
}
+
+// We do not run the CLI in its own process even though that makes it not truly e2e to
+// test whether we're cleaning up state correctly.
+func testMain(dir string, env *xos.Env, args ...string) *xmain.TestState {
+ return &xmain.TestState{
+ Run: d2cli.Run,
+ Env: env,
+ Args: append([]string{"e2etests-cli/d2"}, args...),
+ PWD: dir,
+ }
+}
+
+func runTestMain(tb testing.TB, ctx context.Context, dir string, env *xos.Env, args ...string) error {
+ tms := testMain(dir, env, args...)
+ tms.Start(tb, ctx)
+ defer tms.Cleanup(tb)
+ err := tms.Wait(ctx)
+ if err != nil {
+ return err
+ }
+ removeD2Files(tb, dir)
+ return nil
+}
+
+func writeFile(tb testing.TB, dir, fp, data string) {
+ tb.Helper()
+ err := os.MkdirAll(filepath.Dir(filepath.Join(dir, fp)), 0755)
+ assert.Success(tb, err)
+ assert.WriteFile(tb, filepath.Join(dir, fp), []byte(data), 0644)
+}
+
+func readFile(tb testing.TB, dir, fp string) []byte {
+ tb.Helper()
+ return assert.ReadFile(tb, filepath.Join(dir, fp))
+}
+
+func removeD2Files(tb testing.TB, dir string) {
+ ea, err := os.ReadDir(dir)
+ assert.Success(tb, err)
+
+ for _, e := range ea {
+ if e.IsDir() {
+ removeD2Files(tb, filepath.Join(dir, e.Name()))
+ continue
+ }
+ ext := filepath.Ext(e.Name())
+ if ext == ".d2" {
+ assert.Remove(tb, filepath.Join(dir, e.Name()))
+ }
+ }
+}
+
+func testdataIgnoreDiff(tb testing.TB, ext string, got []byte) {
+ _ = diff.Testdata(filepath.Join("testdata", tb.Name()), ext, got)
+}
diff --git a/e2etests-cli/testdata/TestCLI_E2E/hello_world_png.exp.png b/e2etests-cli/testdata/TestCLI_E2E/hello_world_png.exp.png
new file mode 100644
index 000000000..613ca9a63
Binary files /dev/null and b/e2etests-cli/testdata/TestCLI_E2E/hello_world_png.exp.png differ
diff --git a/e2etests-cli/testdata/TestCLI_E2E/hello_world_png_pad.exp.png b/e2etests-cli/testdata/TestCLI_E2E/hello_world_png_pad.exp.png
new file mode 100644
index 000000000..296288588
Binary files /dev/null and b/e2etests-cli/testdata/TestCLI_E2E/hello_world_png_pad.exp.png differ
diff --git a/e2etests-cli/testdata/TestCLI_E2E/hello_world_png_sketch.exp.png b/e2etests-cli/testdata/TestCLI_E2E/hello_world_png_sketch.exp.png
new file mode 100644
index 000000000..bef8fb071
Binary files /dev/null and b/e2etests-cli/testdata/TestCLI_E2E/hello_world_png_sketch.exp.png differ
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/index.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/index.exp.svg
new file mode 100644
index 000000000..4787423c1
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/index.exp.svg
@@ -0,0 +1,23 @@
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/broker.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/broker.exp.svg
new file mode 100644
index 000000000..adaadb7a2
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/broker.exp.svg
@@ -0,0 +1,23 @@
+mortgagerealtor
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/core.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/core.exp.svg
new file mode 100644
index 000000000..c3aac3445
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/core.exp.svg
@@ -0,0 +1,23 @@
+belieffooddiet
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/stocks.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/stocks.exp.svg
new file mode 100644
index 000000000..bbfda5151
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/stocks.exp.svg
@@ -0,0 +1,23 @@
+TSXNYSENASDAQ
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/scenarios/why.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/scenarios/why.exp.svg
new file mode 100644
index 000000000..e5ed30e0c
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life/scenarios/why.exp.svg
@@ -0,0 +1,23 @@
+xy
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/index.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/index.exp.svg
new file mode 100644
index 000000000..4787423c1
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/index.exp.svg
@@ -0,0 +1,23 @@
+xy
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/broker.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/broker.exp.svg
new file mode 100644
index 000000000..adaadb7a2
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/broker.exp.svg
@@ -0,0 +1,23 @@
+mortgagerealtor
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/core.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/core.exp.svg
new file mode 100644
index 000000000..c3aac3445
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/core.exp.svg
@@ -0,0 +1,23 @@
+belieffooddiet
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/stocks.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/stocks.exp.svg
new file mode 100644
index 000000000..bbfda5151
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/stocks.exp.svg
@@ -0,0 +1,23 @@
+TSXNYSENASDAQ
+
+
+
diff --git a/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/scenarios/why.exp.svg b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/scenarios/why.exp.svg
new file mode 100644
index 000000000..e5ed30e0c
--- /dev/null
+++ b/e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/scenarios/why.exp.svg
@@ -0,0 +1,23 @@
+xy
+
+
+
diff --git a/go.mod b/go.mod
index 34b2a85ea..a3d90206c 100644
--- a/go.mod
+++ b/go.mod
@@ -23,7 +23,7 @@ require (
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
gonum.org/v1/plot v0.12.0
nhooyr.io/websocket v1.8.7
- oss.terrastruct.com/util-go v0.0.0-20230301015829-35b30391c74d
+ oss.terrastruct.com/util-go v0.0.0-20230303051516-f04d4d93bed8
)
require (
diff --git a/go.sum b/go.sum
index 4c1b4fb99..f7c198223 100644
--- a/go.sum
+++ b/go.sum
@@ -277,6 +277,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
-oss.terrastruct.com/util-go v0.0.0-20230301015829-35b30391c74d h1:+1Bp2bYA7bieedJuqbiwOLhnMs6GQQLB4sNX7BcDbSQ=
-oss.terrastruct.com/util-go v0.0.0-20230301015829-35b30391c74d/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU=
+oss.terrastruct.com/util-go v0.0.0-20230303051516-f04d4d93bed8 h1:sXAJ18qH3RfrhvAo3YaGv1mDewEKV8etsFq1KqhbvIM=
+oss.terrastruct.com/util-go v0.0.0-20230303051516-f04d4d93bed8/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4=
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=