Merge pull request #963 from nhooyr/cli-tests-740f-b475
e2etests-cli: Add PNG test
|
|
@ -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 - && \
|
RUN curl -fsSL https://deb.nodesource.com/setup_19.x | bash -s - && \
|
||||||
apt-get install -y nodejs
|
apt-get install -y nodejs
|
||||||
# See https://github.com/microsoft/playwright/issues/18319
|
RUN npx playwright@1.31.1 install --with-deps chromium
|
||||||
RUN npx playwright@1.31.1 install-deps chromium
|
|
||||||
|
|
||||||
RUN adduser --gecos '' --disabled-password debian \
|
RUN adduser --gecos '' --disabled-password debian \
|
||||||
&& echo "debian ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd
|
&& echo "debian ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,6 @@ fi
|
||||||
|
|
||||||
if [ "${CI:-}" ]; then
|
if [ "${CI:-}" ]; then
|
||||||
export FORCE_COLOR=1
|
export FORCE_COLOR=1
|
||||||
|
npx playwright@1.31.1 install --with-deps chromium
|
||||||
fi
|
fi
|
||||||
go test --timeout=30m "$@"
|
go test --timeout=30m "$@"
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,12 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
outputPath = renameExt(inputPath, ".svg")
|
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)
|
match := d2themescatalog.Find(*themeFlag)
|
||||||
if match == (d2themes.Theme{}) {
|
if match == (d2themes.Theme{}) {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,16 @@ package e2etests_cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
func TestCLI_E2E(t *testing.T) {
|
||||||
|
|
@ -11,11 +19,108 @@ func TestCLI_E2E(t *testing.T) {
|
||||||
|
|
||||||
tca := []struct {
|
tca := []struct {
|
||||||
name string
|
name string
|
||||||
run func(t *testing.T, ctx context.Context)
|
skipCI bool
|
||||||
|
run func(t *testing.T, ctx context.Context, dir string, env *xos.Env)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "hello_world",
|
name: "hello_world_png",
|
||||||
run: func(t *testing.T, ctx context.Context) {},
|
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.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
if tc.skipCI && os.Getenv("CI") != "" {
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Minute*5)
|
ctx, cancel := context.WithTimeout(ctx, time.Minute*5)
|
||||||
defer cancel()
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
BIN
e2etests-cli/testdata/TestCLI_E2E/hello_world_png.exp.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
e2etests-cli/testdata/TestCLI_E2E/hello_world_png_pad.exp.png
vendored
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
e2etests-cli/testdata/TestCLI_E2E/hello_world_png_sketch.exp.png
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life/index.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/broker.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/core.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life/layers/stocks.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life/scenarios/why.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 329 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/index.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/broker.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/core.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/layers/stocks.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 328 KiB |
23
e2etests-cli/testdata/TestCLI_E2E/multiboard/life_index_d2/scenarios/why.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 329 KiB |
2
go.mod
generated
|
|
@ -23,7 +23,7 @@ require (
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
|
||||||
gonum.org/v1/plot v0.12.0
|
gonum.org/v1/plot v0.12.0
|
||||||
nhooyr.io/websocket v1.8.7
|
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 (
|
require (
|
||||||
|
|
|
||||||
4
go.sum
generated
|
|
@ -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=
|
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 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
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-20230303051516-f04d4d93bed8 h1:sXAJ18qH3RfrhvAo3YaGv1mDewEKV8etsFq1KqhbvIM=
|
||||||
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/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4=
|
||||||
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
||||||
|
|
|
||||||