2022-11-03 13:54:49 +00:00
package d2compiler_test
import (
"fmt"
"path/filepath"
"strings"
"testing"
2022-12-04 20:47:55 +00:00
tassert "github.com/stretchr/testify/assert"
2022-12-08 07:22:20 +00:00
2023-01-04 01:50:27 +00:00
"oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/util-go/diff"
2024-04-17 21:49:47 +00:00
"oss.terrastruct.com/util-go/mapfs"
2023-01-04 01:50:27 +00:00
2022-11-03 13:54:49 +00:00
"oss.terrastruct.com/d2/d2compiler"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2target"
)
func TestCompile ( t * testing . T ) {
t . Parallel ( )
testCases := [ ] struct {
name string
text string
2024-04-17 21:49:47 +00:00
// For tests that use imports, define `index.d2` as text and other files here
files map [ string ] string
2022-11-03 13:54:49 +00:00
expErr string
assertions func ( t * testing . T , g * d2graph . Graph )
} {
{
name : "basic_shape" ,
text : `
x : {
shape : circle
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatalf ( "expected 1 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Shape . Value != d2target . ShapeCircle {
t . Fatalf ( "expected g.Objects[0].Shape.Value to be circle: %#v" , g . Objects [ 0 ] . Shape . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "basic_style" ,
text : `
x : {
style . opacity : 0.4
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatalf ( "expected 1 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "expected g.Objects[0].Style.Opacity.Value to be 0.4: %#v" , g . Objects [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "image_style" ,
text : ` hey : "" {
icon : https : //icons.terrastruct.com/essentials/004-picture.svg
shape : image
style . stroke : "#0D32B2"
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatalf ( "expected 1 objects: %#v" , g . Objects )
}
} ,
} ,
{
name : "dimensions_on_nonimage" ,
text : ` hey : "" {
shape : hexagon
width : 200
height : 230
}
` ,
2022-12-29 04:47:40 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatalf ( "expected 1 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "hey" {
t . Fatalf ( "expected g.Objects[0].ID to be 'hey': %#v" , g . Objects [ 0 ] )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Shape . Value != d2target . ShapeHexagon {
t . Fatalf ( "expected g.Objects[0].Shape.Value to be hexagon: %#v" , g . Objects [ 0 ] . Shape . Value )
2022-12-29 04:47:40 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . WidthAttr . Value != "200" {
t . Fatalf ( "expected g.Objects[0].Width.Value to be 200: %#v" , g . Objects [ 0 ] . WidthAttr . Value )
2022-12-29 04:47:40 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . HeightAttr . Value != "230" {
t . Fatalf ( "expected g.Objects[0].Height.Value to be 230: %#v" , g . Objects [ 0 ] . HeightAttr . Value )
2022-12-29 04:47:40 +00:00
}
} ,
} ,
2023-02-18 22:54:10 +00:00
{
name : "positions" ,
text : ` hey : {
2023-02-19 00:23:27 +00:00
top : 200
left : 230
2023-02-18 22:54:10 +00:00
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "200" , g . Objects [ 0 ] . Top . Value )
2023-02-18 22:54:10 +00:00
} ,
} ,
2024-12-15 05:12:41 +00:00
{
name : "reserved_missing_values" ,
text : ` foobar : {
width
bottom
left
right
}
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / reserved_missing_values . d2 : 2 : 3 : reserved field "width" must have a value
d2 / testdata / d2compiler / TestCompile / reserved_missing_values . d2 : 4 : 3 : reserved field "left" must have a value ` ,
} ,
2023-02-22 04:09:23 +00:00
{
name : "positions_negative" ,
text : ` hey : {
top : 200
left : - 200
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/positions_negative.d2:3:8: left must be a non-negative integer: "-200" ` ,
} ,
2022-12-29 04:47:40 +00:00
{
name : "equal_dimensions_on_circle" ,
text : ` hey : "" {
shape : circle
width : 200
height : 230
}
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / equal_dimensions_on_circle . d2 : 3 : 2 : width and height must be equal for circle shapes
2023-01-24 11:09:40 +00:00
d2 / testdata / d2compiler / TestCompile / equal_dimensions_on_circle . d2 : 4 : 2 : width and height must be equal for circle shapes ` ,
2022-12-29 04:47:40 +00:00
} ,
{
name : "single_dimension_on_circle" ,
text : ` hey : "" {
shape : circle
height : 230
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatalf ( "expected 1 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "hey" {
t . Fatalf ( "expected ID to be 'hey': %#v" , g . Objects [ 0 ] )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Shape . Value != d2target . ShapeCircle {
t . Fatalf ( "expected Attributes.Shape.Value to be circle: %#v" , g . Objects [ 0 ] . Shape . Value )
2022-12-29 04:47:40 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . WidthAttr != nil {
t . Fatalf ( "expected Attributes.Width to be nil: %#v" , g . Objects [ 0 ] . WidthAttr )
2022-12-29 04:47:40 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . HeightAttr == nil {
2022-12-29 04:47:40 +00:00
t . Fatalf ( "Attributes.Height is nil" )
}
} ,
2022-11-03 13:54:49 +00:00
} ,
2022-12-29 05:14:25 +00:00
{
2023-02-19 05:51:55 +00:00
name : "dimensions_on_containers" ,
2022-12-29 05:14:25 +00:00
text : `
containers : {
circle container : {
shape : circle
width : 512
diamond : {
shape : diamond
width : 128
height : 64
}
}
diamond container : {
shape : diamond
width : 512
height : 256
circle : {
shape : circle
width : 128
}
}
oval container : {
shape : oval
width : 512
height : 256
hexagon : {
shape : hexagon
width : 128
height : 64
}
}
hexagon container : {
shape : hexagon
width : 512
height : 256
oval : {
shape : oval
width : 128
height : 64
}
}
}
` ,
2023-01-06 19:01:45 +00:00
} ,
{
name : "dimension_with_style" ,
text : ` x : {
width : 200
style . multiple : true
}
2022-12-29 05:14:25 +00:00
` ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "basic_icon" ,
text : ` hey : "" {
icon : https : //icons.terrastruct.com/essentials/004-picture.svg
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Icon == nil {
2022-11-03 13:54:49 +00:00
t . Fatal ( "Attribute icon is nil" )
}
} ,
} ,
2023-03-14 03:07:13 +00:00
{
name : "fill-pattern" ,
text : ` x : {
style : {
fill - pattern : dots
}
}
` ,
} ,
{
name : "invalid-fill-pattern" ,
text : ` x : {
style : {
fill - pattern : ddots
}
}
` ,
2024-03-25 21:30:23 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected "fill-pattern" to be one of: none, dots, lines, grain, paper ` ,
2023-03-14 03:07:13 +00:00
} ,
2022-11-03 13:54:49 +00:00
{
name : "shape_unquoted_hex" ,
text : ` x : {
style : {
fill : # ffffff
}
}
` ,
2023-01-18 12:32:12 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/shape_unquoted_hex.d2:3:10: missing value after colon ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "edge_unquoted_hex" ,
text : ` x - > y : {
style : {
fill : # ffffff
}
}
` ,
2023-01-18 12:32:12 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/edge_unquoted_hex.d2:3:10: missing value after colon ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "blank_underscore" ,
text : ` x : {
y
_
}
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/blank_underscore.d2:3:3: field key must contain more than underscores ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "image_non_style" ,
text : ` x : {
shape : image
icon : https : //icons.terrastruct.com/aws/_Group%20Icons/EC2-instance-container_light-bg.svg
name : y
}
` ,
2023-01-18 12:32:12 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/image_non_style.d2:4:3: image shapes cannot have children. ` ,
2022-11-03 13:54:49 +00:00
} ,
2023-03-05 17:43:42 +00:00
{
name : "image_children_Steps" ,
text : ` x : {
icon : https : //icons.terrastruct.com/aws/_Group%20Icons/EC2-instance-container_light-bg.svg
shape : image
Steps
}
` ,
2025-03-23 13:30:24 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/image_children_Steps.d2:4:3: steps must be declared at a board root scope ` ,
2023-03-05 17:43:42 +00:00
} ,
2023-06-12 18:26:51 +00:00
{
name : "name-with-dot-underscore" ,
text : ` A : {
_ . C
}
"D.E" : {
_ . C
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 3 , len ( g . Objects ) )
} ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "stroke-width" ,
text : ` hey {
style . stroke - width : 0
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatalf ( "expected 1 objects: %#v" , g . Objects )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . StrokeWidth . Value != "0" {
2022-11-03 13:54:49 +00:00
t . Fatalf ( "unexpected" )
}
} ,
} ,
{
name : "illegal-stroke-width" ,
text : ` hey {
style . stroke - width : - 1
}
` ,
2023-01-18 12:32:12 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/illegal-stroke-width.d2:2:23: expected "stroke-width" to be a number between 0 and 15 ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "underscore_parent_create" ,
text : `
x : {
_ . y
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Root . ChildrenArray ) != 2 {
t . Fatalf ( "expected 2 objects at the root: %#v" , len ( g . Root . ChildrenArray ) )
}
} ,
} ,
2022-12-04 20:47:55 +00:00
{
name : "underscore_unresolved_obj" ,
text : `
x : {
_ . y
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "y" , g . Objects [ 1 ] . ID )
2023-01-24 05:48:43 +00:00
tassert . Equal ( t , g . Objects [ 0 ] . AbsID ( ) , g . Objects [ 1 ] . References [ 0 ] . ScopeObj . AbsID ( ) )
2022-12-04 20:47:55 +00:00
} ,
} ,
2023-02-15 00:45:55 +00:00
{
name : "underscore_connection" ,
text : ` a : {
_ . c . d - > _ . c . b
}
` ,
2023-02-15 00:48:16 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-02-15 00:52:50 +00:00
tassert . Equal ( t , 4 , len ( g . Objects ) )
2023-02-15 00:48:16 +00:00
tassert . Equal ( t , 1 , len ( g . Edges ) )
} ,
2023-02-15 00:45:55 +00:00
} ,
2022-11-03 13:54:49 +00:00
{
name : "underscore_parent_not_root" ,
text : `
x : {
y : {
_ . z
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Root . ChildrenArray ) != 1 {
t . Fatalf ( "expected 1 object at the root: %#v" , len ( g . Root . ChildrenArray ) )
}
if len ( g . Objects [ 0 ] . ChildrenArray ) != 2 {
t . Fatalf ( "expected 2 objects within x: %v" , len ( g . Objects [ 0 ] . ChildrenArray ) )
}
} ,
} ,
{
name : "underscore_parent_preference_1" ,
text : `
x : {
_ . y : "All we are given is possibilities -- to make ourselves one thing or another."
}
y : "But it's real. And if it's real it can be affected ... we may not be able"
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Root . ChildrenArray ) != 2 {
t . Fatalf ( "expected 2 objects at the root: %#v" , len ( g . Root . ChildrenArray ) )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 1 ] . Label . Value != "But it's real. And if it's real it can be affected ... we may not be able" {
t . Fatalf ( "expected g.Objects[1].Label.Value to be last value: %#v" , g . Objects [ 1 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "underscore_parent_preference_2" ,
text : `
y : "But it's real. And if it's real it can be affected ... we may not be able"
x : {
_ . y : "All we are given is possibilities -- to make ourselves one thing or another."
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[0].ID to be y: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[1].ID to be x: %#v" , g . Objects [ 1 ] )
}
if len ( g . Root . ChildrenArray ) != 2 {
t . Fatalf ( "expected 2 objects at the root: %#v" , len ( g . Root . ChildrenArray ) )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Label . Value != "All we are given is possibilities -- to make ourselves one thing or another." {
t . Fatalf ( "expected g.Objects[0].Label.Value to be last value: %#v" , g . Objects [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "underscore_parent_squared" ,
text : `
x : {
y : {
_ . _ . z
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , len ( g . Objects ) )
}
if len ( g . Root . ChildrenArray ) != 2 {
t . Fatalf ( "expected 2 objects at the root: %#v" , len ( g . Root . ChildrenArray ) )
}
} ,
} ,
{
name : "underscore_parent_root" ,
text : `
_ . x
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/underscore_parent_root.d2:2:1: invalid underscore: no parent ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "underscore_parent_middle_path" ,
text : `
x : {
y . _ . z
}
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/underscore_parent_middle_path.d2:3:5: parent "_" can only be used in the beginning of paths, e.g. "_.x" ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "underscore_parent_sandwich_path" ,
text : `
x : {
_ . z . _
}
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/underscore_parent_sandwich_path.d2:3:7: parent "_" can only be used in the beginning of paths, e.g. "_.x" ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "underscore_edge" ,
text : `
x : {
_ . y - > _ . x
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "y" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be y: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . Dst . ID != "x" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be x: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . SrcArrow {
t . Fatalf ( "expected g.Edges[0].SrcArrow to be false: %#v" , g . Edges [ 0 ] )
}
if ! g . Edges [ 0 ] . DstArrow {
t . Fatalf ( "expected g.Edges[0].DstArrow to be true: %#v" , g . Edges [ 0 ] )
}
} ,
} ,
{
name : "underscore_edge_chain" ,
text : `
x : {
_ . y - > _ . x - > _ . z
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if g . Objects [ 2 ] . ID != "z" {
t . Fatalf ( "expected g.Objects[2].ID to be z: %#v" , g . Objects [ 2 ] )
}
if len ( g . Edges ) != 2 {
t . Fatalf ( "expected 2 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "y" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be y: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . Dst . ID != "x" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be x: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 1 ] . Src . ID != "x" {
t . Fatalf ( "expected g.Edges[1].Src.ID to be x: %#v" , g . Edges [ 1 ] )
}
if g . Edges [ 1 ] . Dst . ID != "z" {
t . Fatalf ( "expected g.Edges[1].Dst.ID to be z: %#v" , g . Edges [ 1 ] )
}
} ,
} ,
2023-04-30 03:45:03 +00:00
{
name : "md_block_string_err" ,
text : ` test : | md
# What about pipes
Will escaping \ | work ?
|
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / md_block_string_err . d2 : 4 : 19 : unexpected text after md block string . See https : //d2lang.com/tour/text#advanced-block-strings.
d2 / testdata / d2compiler / TestCompile / md_block_string_err . d2 : 5 : 1 : block string must be terminated with | ` ,
} ,
2023-06-02 01:48:37 +00:00
{
2023-06-02 17:42:44 +00:00
name : "no_empty_block_string" ,
2023-06-02 01:48:37 +00:00
text : ` Text: |md | ` ,
2023-06-02 17:42:44 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/no_empty_block_string.d2:1:1: block string cannot be empty ` ,
} ,
{
name : "no_white_spaces_only_block_string" ,
text : ` Text: |md | ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/no_white_spaces_only_block_string.d2:1:1: block string cannot be empty ` ,
} ,
{
name : "no_new_lines_only_block_string" ,
text : ` Text : | md
| ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/no_new_lines_only_block_string.d2:1:1: block string cannot be empty ` ,
2023-06-02 01:48:37 +00:00
} ,
2022-11-03 13:54:49 +00:00
{
name : "underscore_edge_existing" ,
text : `
a - > b : "Can you imagine how life could be improved if we could do away with"
x : {
_ . a - > _ . b : "Well, it's garish, ugly, and derelicts have used it for a toilet."
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 2 {
t . Fatalf ( "expected 2 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "a" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be a: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . Dst . ID != "b" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be b: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 1 ] . Src . ID != "a" {
t . Fatalf ( "expected g.Edges[1].Src.ID to be a: %#v" , g . Edges [ 1 ] )
}
if g . Edges [ 1 ] . Dst . ID != "b" {
t . Fatalf ( "expected g.Edges[1].Dst.ID to be b: %#v" , g . Edges [ 1 ] )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "Can you imagine how life could be improved if we could do away with" {
t . Fatalf ( "unexpected g.Edges[0].Label: %#v" , g . Edges [ 0 ] . Label )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 1 ] . Label . Value != "Well, it's garish, ugly, and derelicts have used it for a toilet." {
t . Fatalf ( "unexpected g.Edges[1].Label: %#v" , g . Edges [ 1 ] . Label )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "underscore_edge_index" ,
text : `
a - > b : "Can you imagine how life could be improved if we could do away with"
x : {
( _ . a - > _ . b ) [ 0 ] : "Well, it's garish, ugly, and derelicts have used it for a toilet."
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "a" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be a: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . Dst . ID != "b" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be b: %#v" , g . Edges [ 0 ] )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "Well, it's garish, ugly, and derelicts have used it for a toilet." {
t . Fatalf ( "unexpected g.Edges[0].Label: %#v" , g . Edges [ 0 ] . Label )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
2025-03-04 17:14:32 +00:00
{
name : "legend" ,
text : `
vars : {
d2 - legend : {
User : "A person who interacts with the system" {
shape : person
style : {
fill : "#f5f5f5"
}
}
Database : "Stores application data" {
shape : cylinder
style . fill : "#b5d3ff"
}
HiddenShape : "This should not appear in the legend" {
style . opacity : 0
}
User - > Database : "Reads data" {
style . stroke : "blue"
}
Database - > User : "Returns results" {
style . stroke - dash : 5
}
}
}
user : User
db : Database
user - > db : Uses
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if g . Legend == nil {
t . Fatal ( "Expected Legend to be non-nil" )
return
}
// 2. Verify the correct objects are in the legend
if len ( g . Legend . Objects ) != 2 {
t . Errorf ( "Expected 2 objects in legend, got %d" , len ( g . Legend . Objects ) )
}
// Check for User object
hasUser := false
hasDatabase := false
for _ , obj := range g . Legend . Objects {
if obj . ID == "User" {
hasUser = true
if obj . Shape . Value != "person" {
t . Errorf ( "User shape incorrect, expected 'person', got: %s" , obj . Shape . Value )
}
} else if obj . ID == "Database" {
hasDatabase = true
if obj . Shape . Value != "cylinder" {
t . Errorf ( "Database shape incorrect, expected 'cylinder', got: %s" , obj . Shape . Value )
}
} else if obj . ID == "HiddenShape" {
t . Errorf ( "HiddenShape should not be in legend due to opacity: 0" )
}
}
if ! hasUser {
t . Errorf ( "User object missing from legend" )
}
if ! hasDatabase {
t . Errorf ( "Database object missing from legend" )
}
// 3. Verify the correct edges are in the legend
if len ( g . Legend . Edges ) != 2 {
t . Errorf ( "Expected 2 edges in legend, got %d" , len ( g . Legend . Edges ) )
}
// Check for expected edges
hasReadsEdge := false
hasReturnsEdge := false
for _ , edge := range g . Legend . Edges {
if edge . Label . Value == "Reads data" {
hasReadsEdge = true
// Check edge properties
if edge . Style . Stroke == nil {
t . Errorf ( "Reads edge stroke is nil" )
} else if edge . Style . Stroke . Value != "blue" {
t . Errorf ( "Reads edge stroke incorrect, expected 'blue', got: %s" , edge . Style . Stroke . Value )
}
} else if edge . Label . Value == "Returns results" {
hasReturnsEdge = true
// Check edge properties
if edge . Style . StrokeDash == nil {
t . Errorf ( "Returns edge stroke-dash is nil" )
} else if edge . Style . StrokeDash . Value != "5" {
t . Errorf ( "Returns edge stroke-dash incorrect, expected '5', got: %s" , edge . Style . StrokeDash . Value )
}
} else if edge . Label . Value == "Hidden connection" {
t . Errorf ( "Hidden connection should not be in legend due to opacity: 0" )
}
}
if ! hasReadsEdge {
t . Errorf ( "'Reads data' edge missing from legend" )
}
if ! hasReturnsEdge {
t . Errorf ( "'Returns results' edge missing from legend" )
}
// 4. Verify the regular diagram content is still there
userObj , hasUserObj := g . Root . HasChild ( [ ] string { "user" } )
if ! hasUserObj {
t . Errorf ( "Main diagram missing 'user' object" )
} else if userObj . Label . Value != "User" {
t . Errorf ( "User label incorrect, expected 'User', got: %s" , userObj . Label . Value )
}
dbObj , hasDBObj := g . Root . HasChild ( [ ] string { "db" } )
if ! hasDBObj {
t . Errorf ( "Main diagram missing 'db' object" )
} else if dbObj . Label . Value != "Database" {
t . Errorf ( "DB label incorrect, expected 'Database', got: %s" , dbObj . Label . Value )
}
// Check the main edge
if len ( g . Edges ) == 0 {
t . Errorf ( "No edges found in main diagram" )
} else {
mainEdge := g . Edges [ 0 ]
if mainEdge . Label . Value != "Uses" {
t . Errorf ( "Main edge label incorrect, expected 'Uses', got: %s" , mainEdge . Label . Value )
}
}
} ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "underscore_edge_nested" ,
text : `
x : {
y : {
_ . _ . z - > _ . y
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . AbsID ( ) != "z" {
t . Fatalf ( "expected g.Edges[0].Src.AbsID() to be z: %#v" , g . Edges [ 0 ] . Src . AbsID ( ) )
}
if g . Edges [ 0 ] . Dst . AbsID ( ) != "x.y" {
t . Fatalf ( "expected g.Edges[0].Dst.AbsID() to be x.y: %#v" , g . Edges [ 0 ] . Dst . AbsID ( ) )
}
} ,
} ,
{
name : "edge" ,
text : `
x - > y
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "x" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be x: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . Dst . ID != "y" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be y: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . SrcArrow {
t . Fatalf ( "expected g.Edges[0].SrcArrow to be false: %#v" , g . Edges [ 0 ] )
}
if ! g . Edges [ 0 ] . DstArrow {
t . Fatalf ( "expected g.Edges[0].DstArrow to be true: %#v" , g . Edges [ 0 ] )
}
} ,
} ,
{
name : "edge_chain" ,
text : `
x - > y - > z : "The kids will love our inflatable slides"
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if g . Objects [ 2 ] . ID != "z" {
t . Fatalf ( "expected g.Objects[2].ID to be z: %#v" , g . Objects [ 2 ] )
}
if len ( g . Edges ) != 2 {
t . Fatalf ( "expected 2 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "x" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be x: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . Dst . ID != "y" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be y: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 1 ] . Src . ID != "y" {
t . Fatalf ( "expected g.Edges[1].Src.ID to be x: %#v" , g . Edges [ 1 ] )
}
if g . Edges [ 1 ] . Dst . ID != "z" {
t . Fatalf ( "expected g.Edges[1].Dst.ID to be y: %#v" , g . Edges [ 1 ] )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "The kids will love our inflatable slides" {
t . Fatalf ( "unexpected g.Edges[0].Label: %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 1 ] . Label . Value != "The kids will love our inflatable slides" {
t . Fatalf ( "unexpected g.Edges[1].Label: %#v" , g . Edges [ 1 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_index" ,
text : `
x - > y : one
( x - > y ) [ 0 ] : two
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "x" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be x: %#v" , g . Edges [ 0 ] . Src )
}
if g . Edges [ 0 ] . Dst . ID != "y" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be y: %#v" , g . Edges [ 0 ] . Dst )
}
if g . Edges [ 0 ] . SrcArrow {
t . Fatalf ( "expected g.Edges[0].SrcArrow to be false: %#v" , g . Edges [ 0 ] . SrcArrow )
}
if ! g . Edges [ 0 ] . DstArrow {
t . Fatalf ( "expected g.Edges[0].DstArrow to be true: %#v" , g . Edges [ 0 ] . DstArrow )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "two" {
t . Fatalf ( "expected g.Edges[0].Label to be two: %#v" , g . Edges [ 0 ] . Label )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_index_nested" ,
text : `
b : {
x - > y : one
( x - > y ) [ 0 ] : two
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "b" {
t . Fatalf ( "expected g.Objects[0].ID to be b: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[1].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 2 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[2].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . AbsID ( ) != "b.x" {
t . Fatalf ( "expected g.Edges[0].Src.AbsoluteID() to be x: %#v" , g . Edges [ 0 ] . Src )
}
if g . Edges [ 0 ] . Dst . AbsID ( ) != "b.y" {
t . Fatalf ( "expected g.Edges[0].Dst.AbsoluteID() to be y: %#v" , g . Edges [ 0 ] . Dst )
}
if g . Edges [ 0 ] . SrcArrow {
t . Fatalf ( "expected g.Edges[0].SrcArrow to be false: %#v" , g . Edges [ 0 ] . SrcArrow )
}
if ! g . Edges [ 0 ] . DstArrow {
t . Fatalf ( "expected g.Edges[0].DstArrow to be true: %#v" , g . Edges [ 0 ] . DstArrow )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "two" {
t . Fatalf ( "expected g.Edges[0].Label to be two: %#v" , g . Edges [ 0 ] . Label )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_index_nested_cross_scope" ,
text : `
b : {
x - > y : one
}
b . ( x - > y ) [ 0 ] : two
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "b" {
t . Fatalf ( "expected g.Objects[0].ID to be b: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[1].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 2 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[2].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . AbsID ( ) != "b.x" {
t . Fatalf ( "expected g.Edges[0].Src.AbsoluteID() to be x: %#v" , g . Edges [ 0 ] . Src )
}
if g . Edges [ 0 ] . Dst . AbsID ( ) != "b.y" {
t . Fatalf ( "expected g.Edges[0].Dst.AbsoluteID() to be y: %#v" , g . Edges [ 0 ] . Dst )
}
if g . Edges [ 0 ] . SrcArrow {
t . Fatalf ( "expected g.Edges[0].SrcArrow to be false: %#v" , g . Edges [ 0 ] . SrcArrow )
}
if ! g . Edges [ 0 ] . DstArrow {
t . Fatalf ( "expected g.Edges[0].DstArrow to be true: %#v" , g . Edges [ 0 ] . DstArrow )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "two" {
t . Fatalf ( "expected g.Edges[0].Label to be two: %#v" , g . Edges [ 0 ] . Label )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
2024-11-18 16:00:57 +00:00
{
name : "markdown_ampersand" ,
text : ` memo : | md
< a href = "https://www.google.com/search?q=d2&newwindow=1&bar" > d2 < / a >
|
` ,
} ,
2023-03-07 17:00:45 +00:00
{
name : "unsemantic_markdown" ,
text : ` test : |
foobar
< p >
|
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/unsemantic_markdown.d2:1:1: malformed Markdown: element <p> closed by </div> ` ,
} ,
{
name : "unsemantic_markdown_2" ,
text : ` test : |
foo < br >
bar
|
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/unsemantic_markdown_2.d2:1:1: malformed Markdown: element <br> closed by </p> ` ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "edge_map" ,
text : `
x - > y : {
label : "Space: the final frontier. These are the voyages of the starship Enterprise."
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if g . Objects [ 0 ] . ID != "x" {
t . Fatalf ( "expected g.Objects[0].ID to be x: %#v" , g . Objects [ 0 ] )
}
if g . Objects [ 1 ] . ID != "y" {
t . Fatalf ( "expected g.Objects[1].ID to be y: %#v" , g . Objects [ 1 ] )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if g . Edges [ 0 ] . Src . ID != "x" {
t . Fatalf ( "expected g.Edges[0].Src.ID to be x: %#v" , g . Edges [ 0 ] )
}
if g . Edges [ 0 ] . Dst . ID != "y" {
t . Fatalf ( "expected g.Edges[0].Dst.ID to be y: %#v" , g . Edges [ 0 ] )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_label_map" ,
text : ` hey y9 - > qwer : asdf { style . opacity : 0.5 }
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "asdf" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_map_arrowhead" ,
text : ` x - > y : {
source - arrowhead : {
shape : diamond
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , "diamond" , g . Edges [ 0 ] . SrcArrowhead . Shape . Value )
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Edges [ 0 ] . Shape . Value )
2022-11-03 13:54:49 +00:00
// Make sure the DSL didn't change. this is a regression test where it did
exp := ` x - > y : {
source - arrowhead : {
shape : diamond
}
}
`
newText := d2format . Format ( g . AST )
ds , err := diff . Strings ( exp , newText )
if err != nil {
t . Fatal ( err )
}
if ds != "" {
t . Fatalf ( "exp != newText:\n%s" , ds )
}
} ,
} ,
2023-03-11 21:21:13 +00:00
{
name : "edge_arrowhead_primary" ,
text : ` x - > y : {
source - arrowhead : Reisner ' s Rule of Conceptual Inertia
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . String ( t , "Reisner's Rule of Conceptual Inertia" , g . Edges [ 0 ] . SrcArrowhead . Label . Value )
} ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "edge_arrowhead_fields" ,
text : ` x - > y : {
source - arrowhead : Reisner ' s Rule of Conceptual Inertia {
shape : diamond
}
target - arrowhead : QOTD
target - arrowhead : {
style . filled : true
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , "diamond" , g . Edges [ 0 ] . SrcArrowhead . Shape . Value )
assert . String ( t , "Reisner's Rule of Conceptual Inertia" , g . Edges [ 0 ] . SrcArrowhead . Label . Value )
assert . String ( t , "QOTD" , g . Edges [ 0 ] . DstArrowhead . Label . Value )
assert . String ( t , "true" , g . Edges [ 0 ] . DstArrowhead . Style . Filled . Value )
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Edges [ 0 ] . Shape . Value )
assert . String ( t , "" , g . Edges [ 0 ] . Label . Value )
assert . JSON ( t , nil , g . Edges [ 0 ] . Style . Filled )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "edge_flat_arrowhead" ,
text : ` x - > y
( x - > y ) [ 0 ] . source - arrowhead . shape : diamond
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , "diamond" , g . Edges [ 0 ] . SrcArrowhead . Shape . Value )
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Edges [ 0 ] . Shape . Value )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
// tests setting to an arrowhead-only shape
name : "edge_non_shape_arrowhead" ,
text : ` x - > y : { source - arrowhead . shape : triangle }
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , "triangle" , g . Edges [ 0 ] . SrcArrowhead . Shape . Value )
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Edges [ 0 ] . Shape . Value )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "object_arrowhead_shape" ,
text : ` x : { shape : triangle }
` ,
2023-01-18 12:32:12 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/object_arrowhead_shape.d2:1:5: invalid shape, can only set "triangle" for arrowheads ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "edge_flat_label_arrowhead" ,
text : ` x - > y : {
# comment
source - arrowhead . label : yo
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , "yo" , g . Edges [ 0 ] . SrcArrowhead . Label . Value )
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "edge_semiflat_arrowhead" ,
text : ` x - > y
( x - > y ) [ 0 ] . source - arrowhead : {
shape : diamond
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , "diamond" , g . Edges [ 0 ] . SrcArrowhead . Shape . Value )
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Edges [ 0 ] . Shape . Value )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "edge_mixed_arrowhead" ,
text : ` x - > y : {
target - arrowhead . shape : diamond
}
( x - > y ) [ 0 ] . source - arrowhead : {
shape : diamond
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , "diamond" , g . Edges [ 0 ] . SrcArrowhead . Shape . Value )
assert . String ( t , "diamond" , g . Edges [ 0 ] . DstArrowhead . Shape . Value )
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Edges [ 0 ] . Shape . Value )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "edge_exclusive_style" ,
text : `
x - > y : {
style . animated : true
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Animated . Value != "true" {
t . Fatalf ( "Edges[0].Style.Animated.Value: %#v" , g . Edges [ 0 ] . Style . Animated . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "nested_edge" ,
text : ` sequence - > quest : {
space - > stars
}
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/nested_edge.d2:2:3: cannot create edge inside edge ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "shape_edge_style" ,
text : `
x : {
style . animated : true
}
` ,
} ,
2023-03-07 06:42:52 +00:00
{
name : "edge_invalid_style" ,
text : ` x - > y : {
opacity : 0.5
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/edge_invalid_style.d2:2:3: opacity must be style.opacity ` ,
} ,
{
name : "obj_invalid_style" ,
text : ` x : {
opacity : 0.5
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/obj_invalid_style.d2:2:3: opacity must be style.opacity ` ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "edge_chain_map" ,
text : `
x - > y - > z : {
label : "Space: the final frontier. These are the voyages of the starship Enterprise."
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 2 {
t . Fatalf ( "expected 2 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 1 ] . Label . Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 1 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_index_map" ,
text : `
x - > y
( x - > y ) [ 0 ] : {
label : "Space: the final frontier. These are the voyages of the starship Enterprise."
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_map_nested" ,
text : `
x - > y : {
style : {
opacity : 0.4
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_map_nested_flat" ,
text : `
x - > y : {
style . opacity : 0.4
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_map_group_flat" ,
text : `
x - > y
( x - > y ) [ 0 ] . style . opacity : 0.4
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_map_group_semiflat" ,
text : ` x - > y
( x - > y ) [ 0 ] . style : {
opacity : 0.4
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatalf ( "expected 2 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_key_group_flat_nested" ,
text : `
x : {
a - > b
}
x . ( a - > b ) [ 0 ] . style . opacity : 0.4
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_key_group_flat_nested_underscore" ,
text : `
a - > b
x : {
( _ . a - > _ . b ) [ 0 ] . style . opacity : 0.4
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_key_group_map_nested_underscore" ,
text : `
a - > b
x : {
( _ . a - > _ . b ) [ 0 ] : {
style : {
opacity : 0.4
}
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_key_group_map_flat_nested_underscore" ,
text : `
a - > b
x : {
( _ . a - > _ . b ) [ 0 ] : {
style . opacity : 0.4
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 3 {
t . Fatalf ( "expected 3 objects: %#v" , g . Objects )
}
if len ( g . Edges ) != 1 {
t . Fatalf ( "expected 1 edge: %#v" , g . Edges )
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatalf ( "unexpected g.Edges[0].Style.Opacity.Value: %#v" , g . Edges [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Edges [ 0 ] . Label . Value != "" {
t . Fatalf ( "unexpected g.Edges[0].Label.Value : %#v" , g . Edges [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "edge_map_non_reserved" ,
text : `
x - > y : {
z
}
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/edge_map_non_reserved.d2:3:3: edge map keys must be reserved keywords ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "url_link" ,
text : ` x : {
link : https : //google.com
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Link . Value != "https://google.com" {
2024-12-06 11:15:51 +00:00
t . Fatal ( g . Objects [ 0 ] . Link . Value )
}
} ,
} ,
2025-03-20 15:53:24 +00:00
{
name : "url_relative_link" ,
text : ` x : {
link : / google
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
if g . Objects [ 0 ] . Link . Value != "/google" {
t . Fatal ( g . Objects [ 0 ] . Link . Value )
}
} ,
} ,
2024-12-06 11:15:51 +00:00
{
name : "non_url_link" ,
text : ` x : {
link : vscode : //file//Users/pmoura/logtalk/examples/searching/hill_climbing1.lgt:35:0
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
if g . Objects [ 0 ] . Link . Value != "vscode://file//Users/pmoura/logtalk/examples/searching/hill_climbing1.lgt:35:0" {
2023-04-14 03:04:55 +00:00
t . Fatal ( g . Objects [ 0 ] . Link . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
2024-12-13 05:52:18 +00:00
{
name : "glob-connection-steps" ,
text : ` * . style . stroke : black
layers : {
ok : @ ok
}
` ,
files : map [ string ] string {
"ok.d2" : `
steps : {
1 : {
step1
}
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 0 , len ( g . Steps ) )
assert . Equal ( t , 1 , len ( g . Layers ) )
assert . Equal ( t , 1 , len ( g . Layers [ 0 ] . Steps ) )
} ,
} ,
2025-04-04 00:39:42 +00:00
{
name : "import-nested-var" ,
text : ` ... @ models . environment
` ,
files : map [ string ] string {
"models.d2" : `
vars : {
c : {
k
}
}
environment : {
... $ { c }
}
` ,
} ,
} ,
2025-03-25 14:55:30 +00:00
{
name : "import-connections" ,
text : ` b . c - > b . d
b : @ imp
` ,
files : map [ string ] string {
"imp.d2" : `
c
d
d - > c
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 2 , len ( g . Edges ) )
} ,
} ,
{
name : "import-style-1" ,
2025-03-25 15:01:46 +00:00
text : ` b . c . style . fill : red
2025-03-25 14:55:30 +00:00
b : @ imp
` ,
files : map [ string ] string {
"imp.d2" : ` c ` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 2 , len ( g . Objects ) )
assert . Equal ( t , "c" , g . Objects [ 1 ] . Label . Value )
assert . Equal ( t , "red" , g . Objects [ 1 ] . Style . Fill . Value )
} ,
} ,
{
name : "import-style-2" ,
text : ` b . k . c . style . fill : red
b : @ imp
` ,
files : map [ string ] string {
"imp.d2" : `
k : {
c
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , "c" , g . Objects [ 2 ] . Label . Value )
assert . Equal ( t , "red" , g . Objects [ 2 ] . Style . Fill . Value )
} ,
} ,
2025-03-24 19:44:18 +00:00
{
name : "import-scenario" ,
text : ` a
... @ test
` ,
files : map [ string ] string {
"test.d2" : `
x
scenarios : {
production : {
x . tooltip : foo
}
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 2 , len ( g . Scenarios [ 0 ] . Objects ) )
} ,
} ,
2025-03-24 21:44:41 +00:00
{
name : "import-steps" ,
text : ` a
... @ test
` ,
files : map [ string ] string {
"test.d2" : `
x
steps : {
1 : {
x . tooltip : foo
}
2 : {
x . tooltip : do
}
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 2 , len ( g . Steps [ 0 ] . Objects ) )
assert . Equal ( t , 2 , len ( g . Steps [ 1 ] . Objects ) )
} ,
} ,
2025-03-23 13:25:15 +00:00
{
name : "import-classes-boards" ,
text : ` classes : {
a : {
label : hi
}
}
layers : {
asdf : {
qwer : {
layers : {
ok : {
bok
}
}
}
}
wert : {
classes : @ classes
}
}
` ,
files : map [ string ] string {
"classes.d2" : `
c : {
label : bye
}
` ,
} ,
2025-03-23 13:30:24 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/import-classes-boards.d2:10:7: layers must be declared at a board root scope ` ,
2025-03-23 13:25:15 +00:00
} ,
2024-09-17 19:27:01 +00:00
{
name : "import_url_link" ,
text : ` ... @ test
` ,
files : map [ string ] string {
"test.d2" : ` elem : elem {
link : https : //google.com
} ` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
if g . Objects [ 0 ] . Link . Value != "https://google.com" {
t . Fatal ( g . Objects [ 0 ] . Link . Value )
}
} ,
} ,
2024-09-19 14:57:33 +00:00
{
name : "nested-scope-1" ,
text : ` ... @ second
` ,
files : map [ string ] string {
"second.d2" : ` second : {
... @ third
} ` ,
"third.d2" : ` third : {
elem
} ` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 3 , len ( g . Objects ) )
} ,
} ,
{
name : "nested-scope-2" ,
text : ` ... @ second
a . style . fill : null
` ,
files : map [ string ] string {
"second.d2" : ` a.style.fill: red ` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 1 , len ( g . Objects ) )
} ,
} ,
2023-03-26 16:45:05 +00:00
{
name : "url_tooltip" ,
text : ` x: { tooltip: https://google.com} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Tooltip . Value != "https://google.com" {
t . Fatal ( g . Objects [ 0 ] . Tooltip . Value )
2023-03-26 16:45:05 +00:00
}
} ,
} ,
{
name : "no_url_link_and_url_tooltip_concurrently" ,
text : ` x: { link: https://not-google.com; tooltip: https://google.com} ` ,
2023-03-27 22:07:24 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/no_url_link_and_url_tooltip_concurrently.d2:1:44: Tooltip cannot be set to URL when link is also set (for security) ` ,
2023-03-26 16:45:05 +00:00
} ,
2023-06-09 21:10:04 +00:00
{
name : "url_link_non_url_tooltip_ok" ,
text : ` x: { link: https://not-google.com; tooltip: note: url.ParseRequestURI might see this as a URL} ` ,
expErr : ` ` ,
} ,
2023-03-26 16:45:05 +00:00
{
name : "url_link_and_not_url_tooltip_concurrently" ,
text : ` x: { link: https://google.com; tooltip: hello world} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Link . Value != "https://google.com" {
t . Fatal ( g . Objects [ 0 ] . Link . Value )
2023-03-26 16:45:05 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Tooltip . Value != "hello world" {
t . Fatal ( g . Objects [ 0 ] . Tooltip . Value )
2023-03-26 16:45:05 +00:00
}
} ,
} ,
2024-05-31 06:18:55 +00:00
{
name : "no_url_link_and_path_url_label_concurrently" ,
text : ` x -> y: https://google.com { link: https://not-google.com } ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/no_url_link_and_path_url_label_concurrently.d2:1:35: Label cannot be set to URL when link is also set (for security) ` ,
} ,
{
name : "url_link_and_path_url_label_concurrently" ,
text : ` x -> y: hello world { link: https://google.com} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Edges ) != 1 {
t . Fatal ( len ( g . Edges ) )
}
if g . Edges [ 0 ] . Link . Value != "https://google.com" {
t . Fatal ( g . Edges [ 0 ] . Link . Value )
}
if g . Edges [ 0 ] . Label . Value != "hello world" {
t . Fatal ( g . Edges [ 0 ] . Label . Value )
}
} ,
} ,
2023-02-23 16:49:02 +00:00
{
name : "nil_scope_obj_regression" ,
text : ` a
b : {
_ . a
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
for _ , ref := range g . Objects [ 0 ] . References {
tassert . NotNil ( t , ref . ScopeObj )
}
} ,
} ,
2022-12-25 21:42:11 +00:00
{
name : "near_constant" ,
text : ` x . near : top - center
` ,
2023-02-19 16:15:59 +00:00
} ,
{
name : "near-invalid" ,
text : ` mongodb : MongoDB {
perspective : perspective ( View ) {
password
}
explanation : | md
perspective . model . js
| {
near : mongodb
}
}
a : {
near : a . b
b
}
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / near - invalid . d2 : 9 : 11 : near keys cannot be set to an ancestor
d2 / testdata / d2compiler / TestCompile / near - invalid . d2 : 14 : 9 : near keys cannot be set to an descendant ` ,
2022-12-25 21:42:11 +00:00
} ,
{
name : "near_bad_constant" ,
text : ` x . near : txop - center
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/near_bad_constant.d2:1:9: near key "txop-center" must be the absolute path to a shape or one of the following constants: top-left, top-center, top-right, center-left, center-right, bottom-left, bottom-center, bottom-right ` ,
2022-12-27 05:22:23 +00:00
} ,
2024-02-27 21:16:22 +00:00
{
name : "near_special" ,
text : ` x . near : z . x
z : {
grid - rows : 1
x
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/near_special.d2:1:9: near keys cannot be set to descendants of special objects, like grid cells ` ,
} ,
2022-12-27 05:22:23 +00:00
{
2023-03-22 04:12:04 +00:00
name : "near_bad_connected" ,
2022-12-27 05:22:23 +00:00
2023-03-22 04:12:04 +00:00
text : `
x : {
near : top - center
}
x - > y
` ,
2023-09-29 02:57:34 +00:00
expErr : ` ` ,
2022-12-27 05:22:23 +00:00
} ,
{
2023-03-22 05:43:25 +00:00
name : "near_descendant_connect_to_outside" ,
text : `
x : {
near : top - left
y
}
x . y - > z
` ,
2023-09-29 02:57:34 +00:00
expErr : "" ,
2022-12-25 21:42:11 +00:00
} ,
{
name : "nested_near_constant" ,
text : ` x . y . near : top - center
` ,
2023-01-24 06:45:21 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/nested_near_constant.d2:1:11: constant near keys can only be set on root level shapes ` ,
2022-12-25 21:42:11 +00:00
} ,
2022-11-03 13:54:49 +00:00
{
name : "reserved_icon_near_style" ,
text : ` x : {
icon : orange
style . opacity : 0.5
style . stroke : red
style . fill : green
}
x . near : y
y
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatal ( g . Objects )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . NearKey == nil {
2022-11-03 13:54:49 +00:00
t . Fatal ( "missing near key" )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Icon . Path != "orange" {
t . Fatal ( g . Objects [ 0 ] . Icon )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . Opacity . Value != "0.5" {
t . Fatal ( g . Objects [ 0 ] . Style . Opacity )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . Stroke . Value != "red" {
t . Fatal ( g . Objects [ 0 ] . Style . Stroke )
2022-11-03 13:54:49 +00:00
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . Fill . Value != "green" {
t . Fatal ( g . Objects [ 0 ] . Style . Fill )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
2024-11-22 03:45:59 +00:00
{
name : "reserved_quoted/1" ,
text : ` x : {
"label" : hello
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 2 , len ( g . Objects ) )
2024-11-24 02:40:36 +00:00
assert . Equal ( t , "x" , g . Objects [ 0 ] . Label . Value )
2024-11-22 03:45:59 +00:00
} ,
} ,
{
name : "reserved_quoted/2" ,
text : ` my_table : {
shape : sql_table
width : 200
height : 200
"shape" : string
"icon" : string
"width" : int
"height" : int
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 4 , len ( g . Objects [ 0 ] . SQLTable . Columns ) )
2024-11-24 02:40:36 +00:00
assert . Equal ( t , ` shape ` , g . Objects [ 0 ] . SQLTable . Columns [ 0 ] . Name . Label )
2024-11-22 03:45:59 +00:00
} ,
} ,
{
name : "reserved_quoted/3" ,
text : ` * . "shape"
x
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 2 , len ( g . Objects ) )
2024-11-24 02:40:36 +00:00
assert . Equal ( t , ` x.shape ` , g . Objects [ 0 ] . AbsID ( ) )
2024-11-22 03:45:59 +00:00
} ,
} ,
{
name : "reserved_quoted/4" ,
text : ` x."style"."fill" ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , 3 , len ( g . Objects ) )
} ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "errors/reserved_icon_style" ,
text : ` x : {
near : y
icon : "::????:::%%orange"
style . opacity : - 1
style . opacity : 232
}
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / errors / reserved_icon_style . d2 : 3 : 9 : bad icon url "::????:::%%orange" : parse "::????:::%%orange" : missing protocol scheme
d2 / testdata / d2compiler / TestCompile / errors / reserved_icon_style . d2 : 5 : 18 : expected "opacity" to be a number between 0.0 and 1.0
2023-01-24 06:45:21 +00:00
d2 / testdata / d2compiler / TestCompile / errors / reserved_icon_style . d2 : 2 : 9 : near key "y" must be the absolute path to a shape or one of the following constants : top - left , top - center , top - right , center - left , center - right , bottom - left , bottom - center , bottom - right ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "errors/missing_shape_icon" ,
2023-01-18 12:32:12 +00:00
text : ` x.shape: image ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/errors/missing_shape_icon.d2:1:1: image shape must include an "icon" field ` ,
2022-11-03 13:54:49 +00:00
} ,
{
name : "edge_in_column" ,
text : ` x : {
shape : sql_table
x : { p - > q }
} ` ,
2023-02-17 01:44:54 +00:00
expErr : ` d2 / testdata / d2compiler / TestCompile / edge_in_column . d2 : 3 : 7 : sql_table columns cannot have children
d2 / testdata / d2compiler / TestCompile / edge_in_column . d2 : 3 : 12 : sql_table columns cannot have children ` ,
} ,
{
name : "no-nested-columns-sql" ,
text : ` x : {
shape : sql_table
a -- b . b
} ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/no-nested-columns-sql.d2:3:10: sql_table columns cannot have children ` ,
} ,
{
name : "no-nested-columns-sql-2" ,
text : ` x : {
shape : sql_table
a
}
x . a . b ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/no-nested-columns-sql-2.d2:5:5: sql_table columns cannot have children ` ,
} ,
{
name : "no-nested-columns-class" ,
text : ` x : {
shape : class
a . a
} ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/no-nested-columns-class.d2:3:5: class fields cannot have children ` ,
2022-11-03 13:54:49 +00:00
} ,
2023-04-27 18:22:10 +00:00
{
name : "improper-class-ref" ,
text : ` myobj.class.style.stroke-dash: 3 ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/improper-class-ref.d2:1:7: "class" must be the last part of the key ` ,
} ,
{
name : "tail-style" ,
text : ` myobj.style: 3 ` ,
2023-05-16 17:45:35 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/tail-style.d2:1:7: "style" expected to be set to a map of key-values, or contain an additional keyword like "style.opacity: 0.4" ` ,
} ,
{
name : "tail-style-map" ,
text : ` myobj.style: { } ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/tail-style-map.d2:1:7: "style" expected to be set to a map of key-values, or contain an additional keyword like "style.opacity: 0.4" ` ,
2023-04-27 18:22:10 +00:00
} ,
{
name : "bad-style-nesting" ,
text : ` myobj.style.style.stroke-dash: 3 ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/bad-style-nesting.d2:1:13: invalid style keyword: "style" ` ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "edge_to_style" ,
text : ` x : { style . opacity : 0.4 }
y - > x . style
` ,
2023-01-28 01:19:12 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/edge_to_style.d2:2:8: reserved keywords are prohibited in edges ` ,
2022-11-03 13:54:49 +00:00
} ,
2023-06-01 06:04:09 +00:00
{
name : "keyword-container" ,
text : ` a . near . b
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/keyword-container.d2:1:3: "near" must be the last part of the key ` ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "escaped_id" ,
text : ` b\nb ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
2022-12-05 21:46:42 +00:00
assert . String ( t , ` "b\nb" ` , g . Objects [ 0 ] . ID )
2022-12-01 19:32:57 +00:00
assert . String ( t , ` b
2023-04-14 03:04:55 +00:00
b ` , g . Objects [ 0 ] . Label . Value )
2022-11-03 13:54:49 +00:00
} ,
} ,
2022-12-05 19:40:21 +00:00
{
name : "unescaped_id_cr" ,
text : ` b\rb ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
2022-12-05 21:46:42 +00:00
assert . String ( t , "b\rb" , g . Objects [ 0 ] . ID )
2023-04-14 03:04:55 +00:00
assert . String ( t , "b\rb" , g . Objects [ 0 ] . Label . Value )
2022-12-05 19:40:21 +00:00
} ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "class_style" ,
text : ` IUserProperties : {
shape : "class"
firstName ? : "string"
style . opacity : 0.4
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
if len ( g . Objects [ 0 ] . Class . Fields ) != 1 {
t . Fatal ( len ( g . Objects [ 0 ] . Class . Fields ) )
}
if len ( g . Objects [ 0 ] . Class . Methods ) != 0 {
t . Fatal ( len ( g . Objects [ 0 ] . Class . Methods ) )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatal ( g . Objects [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "table_style" ,
text : ` IUserProperties : {
shape : sql_table
GetType ( ) : string
style . opacity : 0.4
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
if len ( g . Objects [ 0 ] . SQLTable . Columns ) != 1 {
t . Fatal ( len ( g . Objects [ 0 ] . SQLTable . Columns ) )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatal ( g . Objects [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
{
name : "table_style_map" ,
text : ` IUserProperties : {
shape : sql_table
GetType ( ) : string
style : {
opacity : 0.4
2023-01-24 06:45:21 +00:00
font - color : blue
2022-11-03 13:54:49 +00:00
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
if len ( g . Objects [ 0 ] . SQLTable . Columns ) != 1 {
t . Fatal ( len ( g . Objects [ 0 ] . SQLTable . Columns ) )
}
2023-04-14 03:04:55 +00:00
if g . Objects [ 0 ] . Style . Opacity . Value != "0.4" {
t . Fatal ( g . Objects [ 0 ] . Style . Opacity . Value )
2022-11-03 13:54:49 +00:00
}
} ,
} ,
2023-01-13 16:21:32 +00:00
{
name : "table_connection_attr" ,
text : ` x : {
shape : sql_table
y
}
a : {
shape : sql_table
b
}
x . y - > a . b : {
style . animated : true
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "true" , g . Edges [ 0 ] . Style . Animated . Value )
2023-01-13 16:21:32 +00:00
} ,
} ,
2022-11-03 13:54:49 +00:00
{
name : "class_paren" ,
text : ` _shape_ : "shape" {
shape : class
field here
GetType ( ) : string
Is ( ) : bool
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
2022-12-01 19:32:57 +00:00
assert . String ( t , ` field here ` , g . Objects [ 0 ] . Class . Fields [ 0 ] . Name )
assert . String ( t , ` GetType() ` , g . Objects [ 0 ] . Class . Methods [ 0 ] . Name )
assert . String ( t , ` Is() ` , g . Objects [ 0 ] . Class . Methods [ 1 ] . Name )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "sql_paren" ,
text : ` _shape_ : "shape" {
shape : sql_table
GetType ( ) : string
Is ( ) : bool
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 1 {
t . Fatal ( g . Objects )
}
2022-12-20 01:14:39 +00:00
assert . String ( t , ` GetType() ` , g . Objects [ 0 ] . SQLTable . Columns [ 0 ] . Name . Label )
assert . String ( t , ` Is() ` , g . Objects [ 0 ] . SQLTable . Columns [ 1 ] . Name . Label )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "nested_sql" ,
text : ` outer : {
table : {
shape : sql_table
GetType ( ) : string
Is ( ) : bool
}
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
if len ( g . Objects ) != 2 {
t . Fatal ( g . Objects )
}
if _ , has := g . Objects [ 0 ] . HasChild ( [ ] string { "table" } ) ; ! has {
t . Fatal ( g . Objects )
}
if len ( g . Objects [ 0 ] . ChildrenArray ) != 1 {
t . Fatal ( g . Objects )
}
2022-12-20 01:14:39 +00:00
assert . String ( t , ` GetType() ` , g . Objects [ 1 ] . SQLTable . Columns [ 0 ] . Name . Label )
assert . String ( t , ` Is() ` , g . Objects [ 1 ] . SQLTable . Columns [ 1 ] . Name . Label )
2022-11-03 13:54:49 +00:00
} ,
} ,
{
name : "3d_oval" ,
2023-01-24 06:45:21 +00:00
text : ` SVP1 . shape : oval
2022-11-03 13:54:49 +00:00
SVP1 . style .3 d : true ` ,
2023-02-25 03:16:30 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/3d_oval.d2:2:1: key "3d" can only be applied to squares, rectangles, and hexagons ` ,
2022-11-16 17:58:06 +00:00
} , {
name : "edge_column_index" ,
text : ` src : {
shape : sql_table
id : int
dst_id : int
}
dst : {
shape : sql_table
id : int
name : string
}
dst . id <- > src . dst_id
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2022-11-16 18:54:34 +00:00
srcIndex := g . Edges [ 0 ] . SrcTableColumnIndex
if srcIndex == nil || * srcIndex != 0 {
2022-11-16 19:15:14 +00:00
t . Fatalf ( "expected SrcTableColumnIndex to be 0, got %v" , srcIndex )
2022-11-16 17:58:06 +00:00
}
2022-11-16 18:54:34 +00:00
dstIndex := g . Edges [ 0 ] . DstTableColumnIndex
if dstIndex == nil || * dstIndex != 1 {
2022-11-16 19:15:14 +00:00
t . Fatalf ( "expected DstTableColumnIndex to be 1, got %v" , dstIndex )
2022-11-16 17:58:06 +00:00
}
} ,
2022-11-03 13:54:49 +00:00
} ,
2022-11-28 22:10:24 +00:00
{
name : "basic_sequence" ,
text : ` x : {
2022-11-28 22:21:50 +00:00
shape : sequence_diagram
2022-11-28 22:10:24 +00:00
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
assert . String ( t , "sequence_diagram" , g . Objects [ 0 ] . Shape . Value )
2022-11-28 22:10:24 +00:00
} ,
} ,
2023-02-25 03:05:23 +00:00
{
name : "near_sequence" ,
text : ` x : {
shape : sequence_diagram
a
}
b . near : x . a
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/near_sequence.d2:5:9: near keys cannot be set to an object within sequence diagrams ` ,
} ,
2023-02-22 21:21:29 +00:00
{
name : "sequence-timestamp" ,
text : ` shape : sequence_diagram
a
b
"04:20,11:20" : {
"loop through each table" : {
a . "start_time = datetime.datetime.now"
a - > b
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 1 , len ( g . Edges ) )
tassert . Equal ( t , 5 , len ( g . Objects ) )
tassert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
tassert . Equal ( t , "b" , g . Objects [ 1 ] . ID )
2024-11-24 04:41:57 +00:00
tassert . Equal ( t , ` "04:20,11:20" ` , g . Objects [ 2 ] . ID )
2023-02-22 21:21:29 +00:00
tassert . Equal ( t , ` loop through each table ` , g . Objects [ 3 ] . ID )
tassert . Equal ( t , 1 , len ( g . Objects [ 0 ] . ChildrenArray ) )
tassert . Equal ( t , 0 , len ( g . Objects [ 1 ] . ChildrenArray ) )
tassert . Equal ( t , 1 , len ( g . Objects [ 2 ] . ChildrenArray ) )
tassert . True ( t , g . Edges [ 0 ] . ContainedBy ( g . Objects [ 3 ] ) )
} ,
} ,
2022-11-28 22:10:24 +00:00
{
name : "root_sequence" ,
2022-11-28 22:21:50 +00:00
text : ` shape : sequence_diagram
2022-11-28 22:10:24 +00:00
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
assert . String ( t , "sequence_diagram" , g . Root . Shape . Value )
2022-11-28 22:10:24 +00:00
} ,
} ,
2022-12-03 08:06:59 +00:00
{
name : "leaky_sequence" ,
text : ` x : {
shape : sequence_diagram
2022-12-06 03:50:08 +00:00
a
2022-12-03 08:06:59 +00:00
}
b - > x . a
` ,
2023-09-29 02:57:34 +00:00
expErr : ` ` ,
2022-12-03 08:06:59 +00:00
} ,
2022-12-06 03:50:08 +00:00
{
name : "sequence_scoping" ,
text : ` x : {
shape : sequence_diagram
a ; b
group : {
a - > b
a . t1 - > b . t1
b . t1 . t2 - > b . t1
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 7 , len ( g . Objects ) )
tassert . Equal ( t , 3 , len ( g . Objects [ 0 ] . ChildrenArray ) )
} ,
} ,
{
name : "sequence_grouped_note" ,
text : ` shape : sequence_diagram
a ; d
choo : {
d . "this note"
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 4 , len ( g . Objects ) )
tassert . Equal ( t , 3 , len ( g . Root . ChildrenArray ) )
} ,
} ,
2022-11-29 05:39:36 +00:00
{
2023-01-24 11:09:40 +00:00
name : "sequence_container" ,
text : ` shape : sequence_diagram
x . y . q - > j . y . p
ok : {
x . y . q - > j . y . p
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 7 , len ( g . Objects ) )
tassert . Equal ( t , 3 , len ( g . Root . ChildrenArray ) )
} ,
} ,
{
name : "sequence_container_2" ,
text : ` shape : sequence_diagram
x . y . q
ok : {
x . y . q - > j . y . p
meow
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 8 , len ( g . Objects ) )
tassert . Equal ( t , 2 , len ( g . Root . ChildrenArray ) )
} ,
} ,
{
2022-11-30 00:02:37 +00:00
name : "root_direction" ,
2022-11-29 05:39:36 +00:00
2022-11-30 00:02:37 +00:00
text : ` direction: right ` ,
2022-11-29 05:39:36 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
assert . String ( t , "right" , g . Root . Direction . Value )
2022-11-29 05:39:36 +00:00
} ,
} ,
2022-11-29 05:01:42 +00:00
{
2022-11-30 00:02:37 +00:00
name : "default_direction" ,
2022-11-29 05:01:42 +00:00
text : ` x ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
assert . String ( t , "" , g . Objects [ 0 ] . Direction . Value )
2022-11-29 05:01:42 +00:00
} ,
} ,
2022-11-29 05:39:36 +00:00
{
2022-11-30 00:02:37 +00:00
name : "set_direction" ,
2022-11-29 05:39:36 +00:00
text : ` x : {
2022-11-30 00:02:37 +00:00
direction : left
2022-11-29 05:39:36 +00:00
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
assert . String ( t , "left" , g . Objects [ 0 ] . Direction . Value )
2022-11-29 05:39:36 +00:00
} ,
} ,
2022-12-12 00:46:09 +00:00
{
name : "constraint_label" ,
text : ` foo {
label : bar
constraint : BIZ
} ` ,
2023-06-08 18:07:46 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/constraint_label.d2:3:3: "constraint" keyword can only be used in "sql_table" shapes ` ,
2022-12-12 00:46:09 +00:00
} ,
2022-11-29 05:39:36 +00:00
{
2022-11-30 00:02:37 +00:00
name : "invalid_direction" ,
2022-11-29 05:39:36 +00:00
text : ` x : {
2022-11-30 00:02:37 +00:00
direction : diagonal
2022-11-29 05:39:36 +00:00
} ` ,
2023-01-18 12:32:12 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/invalid_direction.d2:2:14: direction must be one of up, down, right, left, got "diagonal" ` ,
2022-11-29 05:39:36 +00:00
} ,
2022-11-30 01:54:25 +00:00
{
name : "self-referencing" ,
text : ` x - > x
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2022-12-03 03:55:49 +00:00
_ , err := diff . Strings ( g . Edges [ 0 ] . Dst . ID , g . Edges [ 0 ] . Src . ID )
if err != nil {
t . Fatal ( err )
}
2022-11-30 01:54:25 +00:00
} ,
} ,
2022-12-23 03:29:13 +00:00
{
name : "null" ,
text : ` null
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2022-12-26 19:18:09 +00:00
tassert . Equal ( t , "'null'" , g . Objects [ 0 ] . ID )
2022-12-23 03:29:13 +00:00
tassert . Equal ( t , "null" , g . Objects [ 0 ] . IDVal )
} ,
} ,
2022-12-23 05:20:18 +00:00
{
name : "sql-regression" ,
text : ` a : {
style : {
fill : lemonchiffon
}
2022-12-23 07:41:15 +00:00
b : {
2022-12-23 05:20:18 +00:00
shape : sql_table
2022-12-23 07:41:15 +00:00
c
2022-12-23 05:20:18 +00:00
}
2022-12-23 05:23:17 +00:00
d
2022-12-23 05:20:18 +00:00
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 3 , len ( g . Objects ) )
} ,
} ,
2022-12-24 21:11:45 +00:00
{
2023-05-01 11:59:57 +00:00
name : "sql-constraints" ,
text : ` x : {
shape : sql_table
a : int { constraint : primary_key }
b : int { constraint : [ primary_key ; foreign_key ] }
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
table := g . Objects [ 0 ] . SQLTable
tassert . Equal ( t , [ ] string { "primary_key" } , table . Columns [ 0 ] . Constraint )
tassert . Equal ( t , [ ] string { "primary_key" , "foreign_key" } , table . Columns [ 1 ] . Constraint )
} ,
2022-12-24 21:11:45 +00:00
} ,
2023-10-12 18:48:27 +00:00
{
name : "sql-null-constraint" ,
text : ` x : {
shape : sql_table
a : int { constraint : null }
b : int { constraint : [ null ] }
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
table := g . Objects [ 0 ] . SQLTable
tassert . Nil ( t , table . Columns [ 0 ] . Constraint )
tassert . Equal ( t , [ ] string { "null" } , table . Columns [ 1 ] . Constraint )
} ,
} ,
2023-01-06 19:03:39 +00:00
{
name : "wrong_column_index" ,
text : ` Chinchillas : {
shape : sql_table
id : int { constraint : primary_key }
whisker_len : int
fur_color : string
age : int
server : int { constraint : foreign_key }
caretaker : int { constraint : foreign_key }
}
Chinchillas_Collectibles : {
shape : sql_table
id : int
collectible : id { constraint : foreign_key }
chinchilla : id { constraint : foreign_key }
}
Chinchillas_Collectibles . chinchilla - > Chinchillas . id ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 0 , * g . Edges [ 0 ] . DstTableColumnIndex )
tassert . Equal ( t , 2 , * g . Edges [ 0 ] . SrcTableColumnIndex )
} ,
} ,
2023-02-05 18:36:38 +00:00
{
name : "link-board-ok" ,
text : ` x . link : layers . x
layers : {
2023-03-01 18:28:01 +00:00
x : {
y
}
2023-02-05 19:04:38 +00:00
} ` ,
2023-03-01 18:28:01 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "root.layers.x" , g . Objects [ 0 ] . Link . Value )
2023-03-01 18:28:01 +00:00
} ,
2023-02-05 19:04:38 +00:00
} ,
{
name : "link-board-mixed" ,
text : ` question : How does the cat go ?
question . link : layers . cat
layers : {
cat : {
the cat - > meeeowwww : goes
}
}
scenarios : {
green : {
2023-03-03 01:56:32 +00:00
question . style . fill : green
2023-02-05 19:04:38 +00:00
}
2023-02-05 18:36:38 +00:00
} ` ,
2023-03-01 18:28:01 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "root.layers.cat" , g . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.cat" , g . Scenarios [ 0 ] . Objects [ 0 ] . Link . Value )
2023-03-01 18:28:01 +00:00
} ,
2023-02-05 18:36:38 +00:00
} ,
2024-11-08 21:26:38 +00:00
{
name : "no-self-link" ,
text : `
x . link : scenarios . a
layers : {
g : {
s . link : _ . layers . g
}
}
scenarios : {
a : {
b
}
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Scenarios [ 0 ] . Objects [ 0 ] . Link )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Layers [ 0 ] . Objects [ 0 ] . Link )
} ,
} ,
2023-02-05 18:36:38 +00:00
{
2024-09-25 15:53:46 +00:00
name : "link-board-not-found-1" ,
2023-02-05 18:36:38 +00:00
text : ` x . link : layers . x
` ,
2024-08-26 23:22:25 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Link )
} ,
2023-02-05 18:36:38 +00:00
} ,
2024-09-25 15:53:46 +00:00
{
name : "link-board-not-found-2" ,
text : ` layers : {
one : {
ping : {
link : two
}
}
two : {
pong : {
link : one
}
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Layers [ 0 ] . Objects [ 0 ] . Link )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Layers [ 1 ] . Objects [ 0 ] . Link )
} ,
} ,
2023-02-05 18:36:38 +00:00
{
name : "link-board-not-board" ,
text : ` zzz
x . link : layers . x . y
layers : {
2023-03-03 01:56:32 +00:00
x : {
2023-02-05 18:36:38 +00:00
y
}
} ` ,
2024-08-26 23:22:25 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Link )
} ,
2023-02-05 18:36:38 +00:00
} ,
{
name : "link-board-nested" ,
text : ` x . link : layers . x . layers . x
layers : {
2023-03-03 01:56:32 +00:00
x : {
2023-02-05 18:36:38 +00:00
layers : {
2023-03-03 01:56:32 +00:00
x : {
2023-03-01 18:28:01 +00:00
hello
2023-03-03 01:56:32 +00:00
}
2023-02-05 18:36:38 +00:00
}
}
} ` ,
2023-03-01 18:28:01 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "root.layers.x.layers.x" , g . Objects [ 0 ] . Link . Value )
2023-03-01 22:53:58 +00:00
} ,
} ,
{
name : "link-board-key-nested" ,
text : ` x : {
y . link : layers . x
}
layers : {
2023-03-03 01:56:32 +00:00
x : {
2023-03-01 22:53:58 +00:00
yo
}
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "root.layers.x" , g . Objects [ 1 ] . Link . Value )
2023-03-01 18:28:01 +00:00
} ,
2023-02-05 18:36:38 +00:00
} ,
2023-03-01 19:27:18 +00:00
{
name : "link-board-underscore" ,
text : ` x
layers : {
x : {
yo
layers : {
2023-03-03 01:56:32 +00:00
x : {
hello . link : _ . _ . layers . x
hey . link : _
}
2023-03-01 19:27:18 +00:00
}
}
} ` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . NotNil ( t , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.x" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.x" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 1 ] . Link . Value )
2023-03-01 19:27:18 +00:00
} ,
} ,
2024-07-18 03:05:53 +00:00
{
name : "link-file-underscore" ,
text : ` ...@x ` ,
files : map [ string ] string {
"x.d2" : ` x
layers : {
a : { c }
b : { d . link : _ . layers . a }
e : {
l
layers : {
j : {
k . link : _
n . link : _ . _
m . link : _ . _ . layers . a
}
}
}
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.a" , g . Layers [ 1 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.e" , g . Layers [ 2 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root" , g . Layers [ 2 ] . Layers [ 0 ] . Objects [ 1 ] . Link . Value )
tassert . Equal ( t , "root.layers.a" , g . Layers [ 2 ] . Layers [ 0 ] . Objects [ 2 ] . Link . Value )
} ,
} ,
2024-08-27 03:27:56 +00:00
{
name : "link-beyond-import-root" ,
text : ` ...@x ` ,
files : map [ string ] string {
"x.d2" : ` x . link : _ . layers . z
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Link )
} ,
} ,
2023-03-01 19:27:18 +00:00
{
name : "link-board-underscore-not-found" ,
text : ` x
layers : {
2023-03-03 01:56:32 +00:00
x : {
yo
2023-03-01 19:27:18 +00:00
layers : {
2023-03-03 01:56:32 +00:00
x : {
hello . link : _ . _ . _
}
2023-03-01 19:27:18 +00:00
}
}
} ` ,
2024-08-26 23:22:25 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link )
} ,
2023-03-01 19:27:18 +00:00
} ,
2024-08-26 20:12:58 +00:00
{
name : "import-icon-near" ,
text : ` y : @ y
` ,
files : map [ string ] string {
"y.d2" : ` syslog
* . icon . near : center - left
` ,
} ,
} ,
2023-03-30 16:43:16 +00:00
{
name : "border-radius-negative" ,
text : ` x
x : {
style . border - radius : - 1
} ` ,
2023-04-13 16:18:28 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/border-radius-negative.d2:3:24: expected "border-radius" to be a number greater or equal to 0 ` ,
2023-03-30 16:43:16 +00:00
} ,
2023-04-08 19:24:29 +00:00
{
name : "text-transform" ,
text : ` direction : right
x - > y : hi {
style : {
text - transform : capitalize
}
}
x . style . text - transform : uppercase
y . style . text - transform : lowercase ` ,
} ,
2023-03-28 18:37:40 +00:00
{
name : "near_near_const" ,
text : `
title : Title {
near : top - center
}
obj {
near : title
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/near_near_const.d2:7:8: near keys cannot be set to an object with a constant near key ` ,
} ,
2023-06-22 23:13:08 +00:00
{
name : "label-near-parent" ,
text : ` hey : sushi {
label . near : outside - top - left
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "sushi" , g . Objects [ 0 ] . Attributes . Label . Value )
tassert . Equal ( t , "outside-top-left" , g . Objects [ 0 ] . Attributes . LabelPosition . Value )
} ,
} ,
{
name : "label-near-composite-separate" ,
text : ` hey : {
label : sushi
label . near : outside - top - left
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "sushi" , g . Objects [ 0 ] . Attributes . Label . Value )
tassert . Equal ( t , "outside-top-left" , g . Objects [ 0 ] . Attributes . LabelPosition . Value )
} ,
} ,
{
name : "label-near-composite-together" ,
text : ` hey : {
label : sushi {
near : outside - top - left
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "sushi" , g . Objects [ 0 ] . Attributes . Label . Value )
tassert . Equal ( t , "outside-top-left" , g . Objects [ 0 ] . Attributes . LabelPosition . Value )
} ,
} ,
{
name : "icon-near-composite-together" ,
text : ` hey : {
icon : https : //asdf.com {
near : outside - top - left
}
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "asdf.com" , g . Objects [ 0 ] . Attributes . Icon . Host )
tassert . Equal ( t , "outside-top-left" , g . Objects [ 0 ] . Attributes . IconPosition . Value )
} ,
} ,
{
name : "label-near-invalid-edge" ,
text : ` hey : {
label : sushi {
near : outside - top - left
a - > b
}
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/label-near-invalid-edge.d2:2:3: unexpected edges in map ` ,
} ,
{
name : "label-near-invalid-field" ,
text : ` hey : {
label : sushi {
near : outside - top - left
a
}
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/label-near-invalid-field.d2:4:3: unexpected field a ` ,
} ,
2023-04-01 00:18:17 +00:00
{
name : "grid" ,
text : ` hey : {
2023-04-06 22:43:03 +00:00
grid - rows : 200
grid - columns : 230
2023-04-01 00:18:17 +00:00
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "200" , g . Objects [ 0 ] . GridRows . Value )
2023-04-01 00:18:17 +00:00
} ,
} ,
{
name : "grid_negative" ,
text : ` hey : {
2023-04-06 22:43:03 +00:00
grid - rows : 200
grid - columns : - 200
2023-04-01 00:18:17 +00:00
}
` ,
2023-04-06 22:43:03 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/grid_negative.d2:3:16: grid-columns must be a positive integer: "-200" ` ,
2023-04-01 00:18:17 +00:00
} ,
2023-04-11 01:21:26 +00:00
{
name : "grid_gap_negative" ,
text : ` hey : {
2023-04-12 20:48:53 +00:00
horizontal - gap : - 200
vertical - gap : - 30
2023-04-11 01:21:26 +00:00
}
` ,
2023-04-12 20:48:53 +00:00
expErr : ` d2 / testdata / d2compiler / TestCompile / grid_gap_negative . d2 : 2 : 18 : horizontal - gap must be a non - negative integer : "-200"
d2 / testdata / d2compiler / TestCompile / grid_gap_negative . d2 : 3 : 16 : vertical - gap must be a non - negative integer : "-30" ` ,
2023-04-11 01:21:26 +00:00
} ,
2023-04-05 00:44:05 +00:00
{
name : "grid_edge" ,
text : ` hey : {
2023-04-06 22:43:03 +00:00
grid - rows : 1
2023-09-13 00:08:34 +00:00
a - > b : ok
2023-04-05 00:44:05 +00:00
}
2023-09-28 18:17:46 +00:00
c - > hey . b
hey . a - > c
hey - > hey . a
2023-04-05 00:44:05 +00:00
2023-09-28 18:17:46 +00:00
hey - > c : ok
2023-04-05 00:44:05 +00:00
` ,
2023-09-29 02:57:34 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/grid_edge.d2:7:1: edge from grid diagram "hey" cannot enter itself ` ,
2023-04-05 00:44:05 +00:00
} ,
2023-09-13 05:17:57 +00:00
{
name : "grid_deeper_edge" ,
text : ` hey : {
grid - rows : 1
a - > b : ok
b : {
2023-09-28 00:13:25 +00:00
c - > d : ok now
2023-09-28 17:54:50 +00:00
c . e - > c . f . g : ok
c . e - > d . h : ok
c - > d . h : ok
2023-09-13 05:17:57 +00:00
}
2023-09-13 05:55:31 +00:00
a : {
grid - columns : 1
2023-09-28 00:13:25 +00:00
e - > f : also ok now
2023-09-28 17:54:50 +00:00
e : {
g - > h : ok
g - > h . h : ok
}
2023-09-29 02:57:34 +00:00
e - > f . i : ok now
e . g - > f . i : ok now
2023-09-13 05:55:31 +00:00
}
2023-09-29 02:57:34 +00:00
a - > b . c : ok now
a . e - > b . c : ok now
2023-09-28 18:17:46 +00:00
a - > a . e : not ok
2023-09-13 05:17:57 +00:00
}
` ,
2023-09-29 02:57:34 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:22:2: edge from grid diagram "hey.a" cannot enter itself ` ,
2023-09-13 05:17:57 +00:00
} ,
2023-09-29 23:19:47 +00:00
{
name : "parent_graph_edge_to_descendant" ,
text : ` tl : {
near : top - left
a . b
}
grid : {
grid - rows : 1
cell . c . d
}
seq : {
shape : sequence_diagram
e . f
}
tl - > tl . a : no
tl - > tl . a . b : no
grid - > grid . cell : no
grid - > grid . cell . c : no
grid . cell - > grid . cell . c : no
grid . cell - > grid . cell . c . d : no
seq - > seq . e : no
seq - > seq . e . f : no
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 13 : 1 : edge from constant near "tl" cannot enter itself
d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 14 : 1 : edge from constant near "tl" cannot enter itself
d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 17 : 1 : edge from grid cell "grid.cell" cannot enter itself
d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 18 : 1 : edge from grid cell "grid.cell" cannot enter itself
d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 15 : 1 : edge from grid diagram "grid" cannot enter itself
d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 16 : 1 : edge from grid diagram "grid" cannot enter itself
d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 19 : 1 : edge from sequence diagram "seq" cannot enter itself
d2 / testdata / d2compiler / TestCompile / parent_graph_edge_to_descendant . d2 : 20 : 1 : edge from sequence diagram "seq" cannot enter itself ` ,
} ,
2023-04-05 04:00:40 +00:00
{
name : "grid_nested" ,
text : ` hey : {
2023-04-06 22:43:03 +00:00
grid - rows : 200
grid - columns : 200
2023-04-05 04:00:40 +00:00
a
b
c
2023-05-09 01:41:47 +00:00
d . valid descendant
e : {
grid - rows : 1
grid - columns : 2
a
b
}
2023-04-05 04:00:40 +00:00
}
` ,
2023-05-09 01:41:47 +00:00
expErr : ` ` ,
2023-04-05 04:00:40 +00:00
} ,
2023-02-06 21:32:08 +00:00
{
name : "classes" ,
text : ` classes : {
dragon_ball : {
label : ""
shape : circle
style . fill : orange
}
path : {
label : "then"
style . stroke - width : 4
}
}
nostar : { class : dragon_ball }
1 star : "*" { class : dragon_ball ; style . fill : red }
2 star : { label : "**" ; class : dragon_ball }
nostar - > 1 star : { class : path }
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 3 , len ( g . Objects ) )
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "dragon_ball" , g . Objects [ 0 ] . Classes [ 0 ] )
tassert . Equal ( t , "" , g . Objects [ 0 ] . Label . Value )
2023-02-06 21:32:08 +00:00
// Class field overrides primary
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "" , g . Objects [ 1 ] . Label . Value )
tassert . Equal ( t , "**" , g . Objects [ 2 ] . Label . Value )
tassert . Equal ( t , "orange" , g . Objects [ 0 ] . Style . Fill . Value )
tassert . Equal ( t , "red" , g . Objects [ 1 ] . Style . Fill . Value )
2023-02-06 21:32:08 +00:00
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "4" , g . Edges [ 0 ] . Style . StrokeWidth . Value )
tassert . Equal ( t , "then" , g . Edges [ 0 ] . Label . Value )
2023-04-27 22:09:43 +00:00
} ,
} ,
{
name : "array-classes" ,
text : ` classes : {
dragon_ball : {
label : ""
shape : circle
style . fill : orange
}
path : {
label : "then"
style . stroke - width : 4
}
path2 : {
style . stroke - width : 2
}
}
nostar : { class : [ dragon_ball ; path ] }
1 star : { class : [ path ; dragon_ball ] }
nostar - > 1 star : { class : [ path ; path2 ] }
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "then" , g . Objects [ 0 ] . Label . Value )
tassert . Equal ( t , "" , g . Objects [ 1 ] . Label . Value )
tassert . Equal ( t , "circle" , g . Objects [ 0 ] . Shape . Value )
tassert . Equal ( t , "circle" , g . Objects [ 1 ] . Shape . Value )
tassert . Equal ( t , "2" , g . Edges [ 0 ] . Style . StrokeWidth . Value )
2023-02-06 21:32:08 +00:00
} ,
} ,
2023-06-07 03:24:47 +00:00
{
name : "comma-array-class" ,
text : ` classes : {
dragon_ball : {
label : ""
shape : circle
style . fill : orange
}
path : {
label : "then"
style . stroke - width : 4
}
}
nostar : { class : [ dragon_ball , path ] } ` ,
expErr : ` d2/testdata/d2compiler/TestCompile/comma-array-class.d2:12:11: class "dragon_ball, path" not found. Did you mean to use ";" to separate array items? ` ,
} ,
2023-02-06 21:32:08 +00:00
{
name : "reordered-classes" ,
text : ` classes : {
x : {
shape : circle
}
}
a . class : x
classes . x . shape : diamond
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 1 , len ( g . Objects ) )
2023-04-14 03:04:55 +00:00
tassert . Equal ( t , "diamond" , g . Objects [ 0 ] . Shape . Value )
2023-02-06 21:32:08 +00:00
} ,
} ,
2023-06-02 00:11:11 +00:00
{
name : "nested-array-classes" ,
text : ` classes : {
one target : {
target - arrowhead . label : 1
}
association : {
target - arrowhead . shape : arrow
}
}
a - > b : { class : [ one target ; association ] }
a - > b : { class : [ association ; one target ] }
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
// They have the same, regardless of order of class application
// since the classes modify attributes exclusive of each other
tassert . Equal ( t , "1" , g . Edges [ 0 ] . DstArrowhead . Label . Value )
tassert . Equal ( t , "1" , g . Edges [ 1 ] . DstArrowhead . Label . Value )
tassert . Equal ( t , "arrow" , g . Edges [ 0 ] . DstArrowhead . Shape . Value )
tassert . Equal ( t , "arrow" , g . Edges [ 1 ] . DstArrowhead . Shape . Value )
} ,
} ,
2023-10-05 20:10:56 +00:00
{
name : "var_in_glob" ,
text : ` vars : {
v : {
ok
}
}
x1 - > x2
x * : {
... $ { v }
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , 4 , len ( g . Objects ) )
2024-09-25 03:41:47 +00:00
tassert . Equal ( t , "x1" , g . Objects [ 0 ] . AbsID ( ) )
tassert . Equal ( t , "x1.ok" , g . Objects [ 1 ] . AbsID ( ) )
tassert . Equal ( t , "x2" , g . Objects [ 2 ] . AbsID ( ) )
tassert . Equal ( t , "x2.ok" , g . Objects [ 3 ] . AbsID ( ) )
2023-10-05 20:10:56 +00:00
} ,
} ,
2025-03-22 02:26:55 +00:00
{
2025-03-22 03:27:17 +00:00
name : "glob-spread-vars/1" ,
2025-03-22 02:26:55 +00:00
text : ` vars : {
b : {
1
}
}
a : {
... $ { b }
2025-03-22 03:27:17 +00:00
* . style . fill : red
2025-03-22 02:26:55 +00:00
}
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
assert . Equal ( t , "1" , g . Objects [ 1 ] . Label . Value )
assert . Equal ( t , "red" , g . Objects [ 1 ] . Style . Fill . Value )
} ,
} ,
2025-03-22 03:27:17 +00:00
{
name : "glob-spread-vars/2" ,
text : ` vars : {
b : {
1
2025-03-22 03:27:55 +00:00
2
2025-03-22 03:27:17 +00:00
}
}
a : {
... $ { b }
* * - > _ . ok
}
ok
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2025-03-22 03:27:55 +00:00
assert . Equal ( t , 2 , len ( g . Edges ) )
2025-03-22 03:27:17 +00:00
} ,
} ,
2025-03-15 01:58:37 +00:00
{
name : "import-var-chain" ,
text : ` ... @ dev
` ,
files : map [ string ] string {
"dev.d2" : `
vars : {
a : {
b
}
c : {
... $ { a }
}
}
` ,
} ,
} ,
2024-11-18 05:13:06 +00:00
{
name : "var_in_markdown" ,
text : ` vars : {
2024-11-18 15:01:47 +00:00
v : ok
2024-11-18 05:13:06 +00:00
}
x : | md
m $ { v } y
2024-11-18 15:01:47 +00:00
` + " ` hey $ { v } ` " + `
regular markdown
` + " ` ` ` " + `
bye $ { v }
` + " ` ` ` " + `
2024-11-18 05:13:06 +00:00
|
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2024-11-18 15:01:47 +00:00
tassert . True ( t , strings . Contains ( g . Objects [ 0 ] . Attributes . Label . Value , "moky" ) )
tassert . False ( t , strings . Contains ( g . Objects [ 0 ] . Attributes . Label . Value , "m${v}y" ) )
// Code spans untouched
tassert . True ( t , strings . Contains ( g . Objects [ 0 ] . Attributes . Label . Value , "hey ${v}" ) )
// Code blocks untouched
tassert . True ( t , strings . Contains ( g . Objects [ 0 ] . Attributes . Label . Value , "bye ${v}" ) )
2024-11-18 05:13:06 +00:00
} ,
} ,
2025-03-24 19:04:46 +00:00
{
name : "var_nested_in_markdown" ,
text : ` vars : {
v : {
g : ok
}
}
x : | md
m $ { v . g } y
|
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . True ( t , strings . Contains ( g . Objects [ 0 ] . Attributes . Label . Value , "moky" ) )
} ,
} ,
2024-08-06 02:18:34 +00:00
{
name : "var_in_vars" ,
text : ` vars : {
Apple : {
shape : circle
label : Granny Smith
}
Cherry : {
shape : circle
label : Rainier Cherry
}
SummerFruit : {
xx : $ { Apple }
cc : $ { Cherry }
xx - > cc
}
}
x : $ { Apple }
c : $ { Cherry }
sf : $ { SummerFruit }
` ,
} ,
2024-09-25 03:17:05 +00:00
{
name : "spread_var_order" ,
text : ` vars : {
before_elem : {
"before_elem"
}
after_elem : {
"after_elem"
}
}
... $ { before_elem }
elem
... $ { after_elem }
` ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "before_elem" , g . Objects [ 0 ] . AbsID ( ) )
tassert . Equal ( t , "elem" , g . Objects [ 1 ] . AbsID ( ) )
tassert . Equal ( t , "after_elem" , g . Objects [ 2 ] . AbsID ( ) )
} ,
} ,
2023-04-26 19:38:41 +00:00
{
name : "class-shape-class" ,
text : ` classes : {
classClass : {
shape : class
}
}
object : {
class : classClass
length ( ) : int
}
` ,
} ,
2023-02-06 21:32:08 +00:00
{
name : "no-class-primary" ,
text : ` x . class
` ,
2024-12-15 05:12:41 +00:00
expErr : ` d2/testdata/d2compiler/TestCompile/no-class-primary.d2:1:3: reserved field "class" must have a value ` ,
2023-02-06 21:32:08 +00:00
} ,
{
name : "no-class-inside-classes" ,
text : ` classes : {
x : {
class : y
}
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/no-class-inside-classes.d2:3:5: "class" cannot appear within "classes" ` ,
} ,
{
// This is okay
name : "missing-class" ,
text : ` x . class : yo
` ,
} ,
{
name : "classes-unreserved" ,
text : ` classes : {
mango : {
seed
}
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/classes-unreserved.d2:3:5: seed is an invalid class field, must be reserved keyword ` ,
} ,
{
name : "classes-internal-edge" ,
text : ` classes : {
mango : {
width : 100
}
jango : {
height : 100
}
mango - > jango
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/classes-internal-edge.d2:8:3: classes cannot contain an edge ` ,
} ,
2023-06-16 23:50:16 +00:00
{
name : "reserved-composite" ,
text : ` shape : sequence_diagram {
alice - > bob : What does it mean \ nto be well - adjusted ?
bob - > alice : The ability to play bridge or \ ngolf as if they were games .
}
` ,
expErr : ` d2/testdata/d2compiler/TestCompile/reserved-composite.d2:1:1: reserved field shape does not accept composite ` ,
} ,
2023-09-18 20:16:50 +00:00
{
name : "text_no_label" ,
text : ` a : "ok" {
shape : text
}
b : " \n " {
shape : text
}
c : "" {
shape : text
}
d : "" {
shape : circle
}
e : " \n "
2023-09-18 20:57:37 +00:00
f : | md |
g : | md
|
2023-09-18 20:16:50 +00:00
` ,
2023-09-18 21:03:32 +00:00
expErr : ` d2 / testdata / d2compiler / TestCompile / text_no_label . d2 : 14 : 1 : block string cannot be empty
d2 / testdata / d2compiler / TestCompile / text_no_label . d2 : 15 : 1 : block string cannot be empty
d2 / testdata / d2compiler / TestCompile / text_no_label . d2 : 4 : 1 : shape text must have a non - empty label
d2 / testdata / d2compiler / TestCompile / text_no_label . d2 : 7 : 1 : shape text must have a non - empty label ` ,
2023-12-14 19:35:35 +00:00
} ,
{
name : "var-not-color" ,
text : ` vars : {
d2 - config : {
theme - overrides : {
B1 : potato
potato : B1
}
}
}
a
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / var - not - color . d2 : 4 : 7 : expected "B1" to be a valid named color ( "orange" ) or a hex code ( "#f0ff3a" )
d2 / testdata / d2compiler / TestCompile / var - not - color . d2 : 5 : 4 : "potato" is not a valid theme code ` ,
2023-09-18 20:16:50 +00:00
} ,
2023-10-28 04:00:16 +00:00
{
name : "no_arrowheads_in_shape" ,
text : ` x . target - arrowhead . shape : cf - one
y . source - arrowhead . shape : cf - one
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / no_arrowheads_in_shape . d2 : 1 : 3 : "target-arrowhead" can only be used on connections
d2 / testdata / d2compiler / TestCompile / no_arrowheads_in_shape . d2 : 2 : 3 : "source-arrowhead" can only be used on connections ` ,
} ,
2024-03-15 17:00:40 +00:00
{
name : "shape-hierarchy" ,
text : ` x : {
shape : hierarchy
a - > b
}
` ,
} ,
2024-03-15 22:26:00 +00:00
{
name : "fixed-pos-shape-hierarchy" ,
text : ` x : {
shape : hierarchy
a - > b
a . top : 20
a . left : 20
}
` ,
expErr : ` d2 / testdata / d2compiler / TestCompile / fixed - pos - shape - hierarchy . d2 : 4 : 2 : position keywords cannot be used with shape "hierarchy"
d2 / testdata / d2compiler / TestCompile / fixed - pos - shape - hierarchy . d2 : 5 : 2 : position keywords cannot be used with shape "hierarchy" ` ,
} ,
2024-04-17 21:49:47 +00:00
{
name : "vars-in-imports" ,
text : ` dev : {
vars : {
env : Dev
}
... @ template . d2
}
qa : {
vars : {
env : Qa
}
... @ template . d2
}
` ,
files : map [ string ] string {
"template.d2" : ` env : {
label : $ { env } Environment
vm : {
label : My Virtual machine !
}
} ` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "dev.env" , g . Objects [ 1 ] . AbsID ( ) )
tassert . Equal ( t , "Dev Environment" , g . Objects [ 1 ] . Label . Value )
2024-09-19 14:57:33 +00:00
tassert . Equal ( t , "qa.env" , g . Objects [ 2 ] . AbsID ( ) )
tassert . Equal ( t , "Qa Environment" , g . Objects [ 2 ] . Label . Value )
2024-04-17 21:49:47 +00:00
} ,
} ,
2024-06-05 21:11:07 +00:00
{
name : "spread-import-link" ,
text : ` k
layers : {
x : { ... @ x }
} ` ,
files : map [ string ] string {
"x.d2" : ` a . link : layers . b
layers : {
b : {
d
}
2024-07-13 14:01:57 +00:00
} ` ,
} ,
2024-07-13 14:38:57 +00:00
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.x.layers.b" , g . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
} ,
} ,
{
2024-07-19 21:18:48 +00:00
name : "import-link-underscore-1" ,
2024-07-13 14:38:57 +00:00
text : ` k
layers : {
x : { ... @ x }
} ` ,
files : map [ string ] string {
"x.d2" : ` a
layers : {
b : {
d . link : _
2024-07-13 23:07:09 +00:00
s . link : _ . layers . k
2024-07-13 14:38:57 +00:00
layers : {
c : {
c . link : _
z . link : _ . _
f . link : _ . _ . layers . b
}
}
}
2024-07-13 23:07:09 +00:00
k : {
k
}
2024-07-19 21:18:48 +00:00
} ` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.x" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.x.layers.b" , g . Layers [ 0 ] . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.x" , g . Layers [ 0 ] . Layers [ 0 ] . Layers [ 0 ] . Objects [ 1 ] . Link . Value )
tassert . Equal ( t , "root.layers.x.layers.b" , g . Layers [ 0 ] . Layers [ 0 ] . Layers [ 0 ] . Objects [ 2 ] . Link . Value )
tassert . Equal ( t , "root.layers.x.layers.k" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 1 ] . Link . Value )
} ,
} ,
{
name : "import-link-underscore-2" ,
text : ` k
layers : {
x : @ x
} ` ,
files : map [ string ] string {
"x.d2" : ` a
layers : {
b : {
d . link : _
s . link : _ . layers . k
layers : {
c : {
c . link : _
z . link : _ . _
f . link : _ . _ . layers . b
}
}
}
k : {
k
}
2024-07-13 14:38:57 +00:00
} ` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.x" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.x.layers.b" , g . Layers [ 0 ] . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
tassert . Equal ( t , "root.layers.x" , g . Layers [ 0 ] . Layers [ 0 ] . Layers [ 0 ] . Objects [ 1 ] . Link . Value )
tassert . Equal ( t , "root.layers.x.layers.b" , g . Layers [ 0 ] . Layers [ 0 ] . Layers [ 0 ] . Objects [ 2 ] . Link . Value )
2024-07-13 23:07:09 +00:00
tassert . Equal ( t , "root.layers.x.layers.k" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 1 ] . Link . Value )
2024-07-13 14:38:57 +00:00
} ,
2024-07-13 14:01:57 +00:00
} ,
2024-09-06 20:58:29 +00:00
{
name : "import-link-underscore-3" ,
text : ` k
layers : {
x : @ x
b : {
b
}
} ` ,
files : map [ string ] string {
"x.d2" : ` a
layers : {
y : @ y
} ` ,
"y.d2" : ` o . link : _ . _ . layers . b
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.b" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
} ,
} ,
2024-07-19 21:03:22 +00:00
{
name : "invalid-link-1" ,
text : ` k
layers : {
x : { ... @ x }
} ` ,
files : map [ string ] string {
"x.d2" : ` k
layers : {
y . link : @ n
} ` ,
"n.d2" : ` n ` ,
} ,
expErr : ` d2/testdata/d2compiler/TestCompile/x.d2:3:5: a board itself cannot be linked; only objects within a board can be linked ` ,
} ,
{
name : "invalid-link-2" ,
text : ` k
layers : {
x : @ x
} ` ,
files : map [ string ] string {
"x.d2" : ` k
layers : {
y . link : @ n
} ` ,
"n.d2" : ` n ` ,
} ,
expErr : ` d2/testdata/d2compiler/TestCompile/x.d2:3:5: a board itself cannot be linked; only objects within a board can be linked ` ,
} ,
2024-07-13 14:01:57 +00:00
{
name : "import-nested-layers" ,
text : ` k
layers : {
x : { ... @ x }
} ` ,
files : map [ string ] string {
"x.d2" : ` a
layers : {
b : {
d
layers : {
c : {
c
}
}
}
2024-06-05 21:11:07 +00:00
} ` ,
} ,
} ,
2024-07-15 21:06:25 +00:00
{
name : "multiple-import-nested-layers" ,
text : ` k
layers : {
x : { ... @ y / x }
} ` ,
files : map [ string ] string {
"y/x.d2" : ` a . c . link : layers . b
layers : {
b : { ... @ n }
} ` ,
"y/n.d2" : "p" ,
} ,
} ,
2024-08-26 19:43:56 +00:00
{
name : "import-link-layer-1" ,
text : ` k
layers : {
x : { ... @ y }
z : { hi }
} ` ,
files : map [ string ] string {
"y.d2" : ` a . link : _ . layers . z
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.z" , g . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
} ,
} ,
{
name : "import-link-layer-2" ,
text : ` ... @ y
layers : {
z : { hi }
} ` ,
files : map [ string ] string {
"y.d2" : ` a . link : layers . z
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.z" , g . Objects [ 0 ] . Link . Value )
} ,
} ,
{
name : "import-link-layer-3" ,
text : ` k
layers : {
x : { ... @ y }
z : { hi }
} ` ,
files : map [ string ] string {
"y.d2" : ` a
layers : {
lol : {
asdf . link : _ . _ . layers . z
}
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.z" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
} ,
} ,
{
name : "import-link-layer-4" ,
text : ` k
layers : {
x : @ y
z : { hi }
} ` ,
files : map [ string ] string {
"y.d2" : ` a
layers : {
lol : {
asdf . link : _ . layers . z
}
z : { fjf }
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
tassert . Equal ( t , "root.layers.x.layers.z" , g . Layers [ 0 ] . Layers [ 0 ] . Objects [ 0 ] . Link . Value )
} ,
} ,
2024-08-13 14:36:22 +00:00
{
name : "sql-table-header-newline" ,
text : ` x : {
shape : sql_table
label : hello \ nworld
}
y : "hello\nworld" {
shape : sql_table
hi : there
}
` ,
2024-08-14 00:40:46 +00:00
expErr : ` d2 / testdata / d2compiler / TestCompile / sql - table - header - newline . d2 : 3 : 3 : shape sql_table cannot have newlines in label
d2 / testdata / d2compiler / TestCompile / sql - table - header - newline . d2 : 6 : 1 : shape sql_table cannot have newlines in label ` ,
2024-08-13 14:36:22 +00:00
} ,
2024-08-17 01:04:01 +00:00
{
name : "sequence-diagram-icons" ,
text : ` shape : sequence_diagram
svc_1 : {
icon : https : //icons.terrastruct.com/dev%2Fdocker.svg
shape : image
}
a : A
b : B
svc_1 . t1 - > a : do with A
svc_1 . "think about A"
svc_1 . t2 - > b : do with B
` ,
} ,
2025-01-08 23:42:44 +00:00
{
name : "layer-import-nested-layer" ,
2025-01-08 23:52:26 +00:00
text : ` layers : {
ok : { ... @ meow }
}
2025-01-08 23:42:44 +00:00
` ,
files : map [ string ] string {
"meow.d2" : ` layers : {
1 : {
asdf
}
}
` ,
} ,
assertions : func ( t * testing . T , g * d2graph . Graph ) {
2025-01-08 23:52:26 +00:00
tassert . Equal ( t , "d2/testdata/d2compiler/TestCompile/layer-import-nested-layer.d2" , g . Layers [ 0 ] . AST . Range . Path )
tassert . Equal ( t , "d2/testdata/d2compiler/TestCompile/meow.d2" , g . Layers [ 0 ] . Layers [ 0 ] . AST . Range . Path )
2025-01-08 23:42:44 +00:00
} ,
} ,
2022-11-03 13:54:49 +00:00
}
for _ , tc := range testCases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
2024-04-17 21:49:47 +00:00
opts := & d2compiler . CompileOptions { }
if tc . files != nil {
tc . files [ "index.d2" ] = tc . text
renamed := make ( map [ string ] string )
for file , content := range tc . files {
renamed [ fmt . Sprintf ( "d2/testdata/d2compiler/TestCompile/%v" , file ) ] = content
}
fs , err := mapfs . New ( renamed )
assert . Success ( t , err )
t . Cleanup ( func ( ) {
err = fs . Close ( )
assert . Success ( t , err )
} )
opts . FS = fs
}
2022-11-03 13:54:49 +00:00
d2Path := fmt . Sprintf ( "d2/testdata/d2compiler/%v.d2" , t . Name ( ) )
2024-04-17 21:49:47 +00:00
g , _ , err := d2compiler . Compile ( d2Path , strings . NewReader ( tc . text ) , opts )
2022-11-03 13:54:49 +00:00
if tc . expErr != "" {
if err == nil {
t . Fatalf ( "expected error with: %q" , tc . expErr )
}
ds , err := diff . Strings ( tc . expErr , err . Error ( ) )
if err != nil {
t . Fatal ( err )
}
if ds != "" {
t . Fatalf ( "unexpected error: %s" , ds )
}
} else if err != nil {
t . Fatal ( err )
}
if tc . expErr == "" && tc . assertions != nil {
t . Run ( "assertions" , func ( t * testing . T ) {
tc . assertions ( t , g )
} )
}
got := struct {
Graph * d2graph . Graph ` json:"graph" `
Err error ` json:"err" `
} {
Graph : g ,
Err : err ,
}
2022-12-01 19:32:57 +00:00
err = diff . TestdataJSON ( filepath . Join ( ".." , "testdata" , "d2compiler" , t . Name ( ) ) , got )
assert . Success ( t , err )
2022-11-03 13:54:49 +00:00
} )
}
}
2023-01-04 01:50:27 +00:00
func TestCompile2 ( t * testing . T ) {
t . Parallel ( )
2023-01-28 01:19:12 +00:00
t . Run ( "boards" , testBoards )
2023-02-02 18:30:54 +00:00
t . Run ( "seqdiagrams" , testSeqDiagrams )
2023-06-16 00:39:05 +00:00
t . Run ( "nulls" , testNulls )
2023-07-11 02:24:21 +00:00
t . Run ( "vars" , testVars )
2023-08-16 06:33:50 +00:00
t . Run ( "globs" , testGlobs )
2023-01-04 01:50:27 +00:00
}
2023-01-28 01:19:12 +00:00
func testBoards ( t * testing . T ) {
2023-01-04 01:50:27 +00:00
t . Parallel ( )
tca := [ ] struct {
name string
run func ( t * testing . T )
} {
{
2023-01-24 05:48:43 +00:00
name : "root" ,
2023-01-04 01:50:27 +00:00
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , ` base
2023-01-04 01:50:27 +00:00
layers : {
one : {
santa
}
two : {
clause
}
}
` , "" )
2023-02-27 21:34:03 +00:00
assert . Equal ( t , 2 , len ( g . Layers ) )
assert . Equal ( t , "one" , g . Layers [ 0 ] . Name )
assert . Equal ( t , "two" , g . Layers [ 1 ] . Name )
2023-01-04 01:50:27 +00:00
} ,
} ,
2023-01-24 05:48:43 +00:00
{
name : "recursive" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , ` base
2023-01-24 05:48:43 +00:00
layers : {
one : {
santa
}
two : {
clause
steps : {
seinfeld : {
reindeer
}
missoula : {
montana
}
}
}
}
` , "" )
assert . Equal ( t , 2 , len ( g . Layers ) )
assert . Equal ( t , "one" , g . Layers [ 0 ] . Name )
assert . Equal ( t , "two" , g . Layers [ 1 ] . Name )
assert . Equal ( t , 2 , len ( g . Layers [ 1 ] . Steps ) )
} ,
} ,
2023-02-27 21:34:03 +00:00
{
2023-02-27 22:33:33 +00:00
name : "isFolderOnly" ,
2023-02-27 21:34:03 +00:00
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-02-27 21:34:03 +00:00
layers : {
one : {
santa
}
two : {
clause
scenarios : {
seinfeld : {
}
missoula : {
steps : {
missus : one two three
}
}
}
}
}
` , "" )
2023-02-27 22:33:33 +00:00
assert . True ( t , g . IsFolderOnly )
2023-02-27 21:34:03 +00:00
assert . Equal ( t , 2 , len ( g . Layers ) )
assert . Equal ( t , "one" , g . Layers [ 0 ] . Name )
assert . Equal ( t , "two" , g . Layers [ 1 ] . Name )
assert . Equal ( t , 2 , len ( g . Layers [ 1 ] . Scenarios ) )
2023-02-27 22:33:33 +00:00
assert . False ( t , g . Layers [ 1 ] . Scenarios [ 0 ] . IsFolderOnly )
assert . False ( t , g . Layers [ 1 ] . Scenarios [ 1 ] . IsFolderOnly )
2023-02-27 21:34:03 +00:00
} ,
} ,
2023-07-29 16:56:36 +00:00
{
name : "isFolderOnly-shapes" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
direction : right
steps : {
1 : {
RJ
}
}
` , "" )
assert . True ( t , g . IsFolderOnly )
} ,
} ,
2024-10-30 02:38:21 +00:00
{
name : "board-label-primary" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , ` hi
layers : {
1 : one {
RJ
}
2 : {
label : two
RJ
}
}
` , "" )
assert . Equal ( t , "one" , g . Layers [ 0 ] . Root . Label . Value )
assert . Equal ( t , "two" , g . Layers [ 1 ] . Root . Label . Value )
} ,
} ,
2024-02-15 01:59:34 +00:00
{
name : "no-inherit-label" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
label : hi
steps : {
1 : {
RJ
}
}
` , "" )
assert . True ( t , g . Root . Label . MapKey != nil )
assert . True ( t , g . Steps [ 0 ] . Root . Label . MapKey == nil )
} ,
} ,
2023-03-25 19:06:06 +00:00
{
name : "scenarios_edge_index" ,
run : func ( t * testing . T ) {
assertCompile ( t , ` a - > x
scenarios : {
1 : {
( a - > x ) [ 0 ] . style . opacity : 0.1
}
}
` , "" )
} ,
} ,
2023-01-27 18:41:25 +00:00
{
2023-01-28 01:19:12 +00:00
name : "errs/duplicate_board" ,
2023-01-27 18:41:25 +00:00
run : func ( t * testing . T ) {
assertCompile ( t , ` base
layers : {
one : {
santa
}
}
steps : {
one : {
clause
}
}
2023-01-28 01:19:12 +00:00
` , ` d2 / testdata / d2compiler / TestCompile2 / boards / errs / duplicate_board . d2 : 9 : 2 : board name one already used by another board ` )
2023-01-27 18:41:25 +00:00
} ,
} ,
2024-11-04 22:58:06 +00:00
{
name : "style-nested-boards" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , ` * * . style . stroke : black
scenarios : {
a : {
x
}
b : {
x
}
}
steps : {
c : {
x
}
d : {
x
}
}
layers : {
e : {
x
}
}
` , ` ` )
assert . Equal ( t , "black" , g . Scenarios [ 0 ] . Objects [ 0 ] . Style . Stroke . Value )
assert . Equal ( t , "black" , g . Scenarios [ 1 ] . Objects [ 0 ] . Style . Stroke . Value )
assert . Equal ( t , "black" , g . Steps [ 0 ] . Objects [ 0 ] . Style . Stroke . Value )
assert . Equal ( t , "black" , g . Steps [ 1 ] . Objects [ 0 ] . Style . Stroke . Value )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Layers [ 0 ] . Objects [ 0 ] . Style . Stroke )
} ,
} ,
2023-01-04 01:50:27 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
tc . run ( t )
} )
}
}
2023-02-02 18:30:54 +00:00
func testSeqDiagrams ( t * testing . T ) {
t . Parallel ( )
t . Run ( "errs" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "sequence_diagram_edge_between_edge_groups" ,
// New sequence diagram scoping implementation is disabled.
skip : true ,
run : func ( t * testing . T ) {
assertCompile ( t , `
Office chatter : {
shape : sequence_diagram
alice : Alice
bob : Bobby
awkward small talk : {
alice - > bob : uhm , hi
bob - > alice : oh , hello
icebreaker attempt : {
alice - > bob : what did you have for lunch ?
}
unfortunate outcome : {
bob - > alice : that ' s personal
}
}
awkward small talk . icebreaker attempt . alice - > awkward small talk . unfortunate outcome . bob
}
` , "d2/testdata/d2compiler/TestCompile2/seqdiagrams/errs/sequence_diagram_edge_between_edge_groups.d2:16:3: edges between edge groups are not allowed" )
} ,
} ,
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
}
2023-06-16 00:39:05 +00:00
func testNulls ( t * testing . T ) {
t . Parallel ( )
t . Run ( "basic" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "shape" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-16 00:39:05 +00:00
a
a : null
` , "" )
assert . Equal ( t , 0 , len ( g . Objects ) )
} ,
} ,
2023-06-26 03:35:56 +00:00
{
2025-01-31 21:32:27 +00:00
name : "basic-edge" ,
2023-06-26 03:35:56 +00:00
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 03:35:56 +00:00
a - > b
( a - > b ) [ 0 ] : null
` , "" )
2023-06-26 17:36:12 +00:00
assert . Equal ( t , 2 , len ( g . Objects ) )
2023-06-26 03:35:56 +00:00
assert . Equal ( t , 0 , len ( g . Edges ) )
} ,
} ,
2025-01-31 21:32:27 +00:00
{
name : "nested-edge" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a . b . c - > a . d . e
a . b . c - > a . d . e
a . ( b . c - > d . e ) [ 0 ] : null
( a . b . c - > a . d . e ) [ 1 ] : null
` , "" )
assert . Equal ( t , 5 , len ( g . Objects ) )
assert . Equal ( t , 0 , len ( g . Edges ) )
} ,
} ,
2023-06-26 19:15:24 +00:00
{
name : "attribute" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 19:15:24 +00:00
a . style . opacity : 0.2
a . style . opacity : null
` , "" )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Attributes . Style . Opacity )
} ,
} ,
2023-06-16 00:39:05 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
t . Run ( "reappear" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "shape" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-16 00:39:05 +00:00
a
a : null
a
` , "" )
assert . Equal ( t , 1 , len ( g . Objects ) )
} ,
} ,
2023-06-26 17:36:12 +00:00
{
name : "edge" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 17:36:12 +00:00
a - > b
( a - > b ) [ 0 ] : null
a - > b
` , "" )
assert . Equal ( t , 2 , len ( g . Objects ) )
assert . Equal ( t , 1 , len ( g . Edges ) )
} ,
} ,
2023-06-26 18:57:18 +00:00
{
name : "attribute-reset" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 18:57:18 +00:00
a . style . opacity : 0.2
a : null
a
` , "" )
assert . Equal ( t , 1 , len ( g . Objects ) )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Attributes . Style . Opacity )
} ,
} ,
{
name : "edge-reset" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 18:57:18 +00:00
a - > b
a : null
a
` , "" )
assert . Equal ( t , 2 , len ( g . Objects ) )
assert . Equal ( t , 0 , len ( g . Edges ) )
} ,
} ,
{
name : "children-reset" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 18:57:18 +00:00
a . b . c
a . b : null
a . b
` , "" )
assert . Equal ( t , 2 , len ( g . Objects ) )
} ,
} ,
2023-06-16 00:39:05 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
2023-06-26 06:10:56 +00:00
t . Run ( "implicit" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
2023-06-26 06:11:47 +00:00
name : "delete-connection" ,
2023-06-26 06:10:56 +00:00
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 06:10:56 +00:00
x - > y
y : null
` , "" )
assert . Equal ( t , 1 , len ( g . Objects ) )
assert . Equal ( t , 0 , len ( g . Edges ) )
} ,
} ,
2024-02-09 06:21:56 +00:00
{
name : "delete-nested-connection" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a - > b . c
b . c : null
` , "" )
assert . Equal ( t , 2 , len ( g . Objects ) )
assert . Equal ( t , 0 , len ( g . Edges ) )
} ,
} ,
2023-07-03 22:58:22 +00:00
{
name : "delete-multiple-connections" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-03 22:58:22 +00:00
x - > y
z - > y
y - > a
y : null
` , "" )
assert . Equal ( t , 3 , len ( g . Objects ) )
assert . Equal ( t , 0 , len ( g . Edges ) )
} ,
} ,
2023-06-26 06:11:47 +00:00
{
name : "no-delete-connection" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 06:11:47 +00:00
y : null
x - > y
` , "" )
assert . Equal ( t , 2 , len ( g . Objects ) )
assert . Equal ( t , 1 , len ( g . Edges ) )
} ,
} ,
2023-06-26 17:36:12 +00:00
{
name : "delete-children" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 17:36:12 +00:00
x . y . z
a . b . c
x : null
a . b : null
` , "" )
assert . Equal ( t , 1 , len ( g . Objects ) )
} ,
} ,
2023-06-26 06:10:56 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
2023-06-26 18:57:18 +00:00
t . Run ( "multiboard" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "scenario" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-06-26 18:57:18 +00:00
x
scenarios : {
a : {
x : null
}
}
` , "" )
assert . Equal ( t , 0 , len ( g . Scenarios [ 0 ] . Objects ) )
} ,
} ,
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
2023-06-16 00:39:05 +00:00
}
2023-07-11 02:24:21 +00:00
func testVars ( t * testing . T ) {
t . Parallel ( )
t . Run ( "basic" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
2023-07-12 00:19:34 +00:00
name : "shape-label" ,
2023-07-11 02:24:21 +00:00
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 02:24:21 +00:00
vars : {
x : im a var
}
hi : $ { x }
` , "" )
assert . Equal ( t , 1 , len ( g . Objects ) )
assert . Equal ( t , "im a var" , g . Objects [ 0 ] . Label . Value )
} ,
} ,
2023-07-11 05:02:58 +00:00
{
name : "style" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 05:02:58 +00:00
vars : {
primary - color : red
}
hi : {
style . fill : $ { primary - color }
}
` , "" )
assert . Equal ( t , 1 , len ( g . Objects ) )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Style . Fill . Value )
} ,
} ,
{
name : "number" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 05:02:58 +00:00
vars : {
columns : 2
}
hi : {
grid - columns : $ { columns }
x
}
` , "" )
assert . Equal ( t , "2" , g . Objects [ 0 ] . GridColumns . Value )
} ,
} ,
{
name : "nested" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 05:02:58 +00:00
vars : {
colors : {
primary : {
button : red
}
}
}
hi : {
style . fill : $ { colors . primary . button }
}
` , "" )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Style . Fill . Value )
} ,
} ,
2023-07-11 04:18:18 +00:00
{
name : "combined" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 04:18:18 +00:00
vars : {
x : im a var
}
hi : 1 $ { x } 2
` , "" )
assert . Equal ( t , "1 im a var 2" , g . Objects [ 0 ] . Label . Value )
} ,
} ,
2023-07-11 20:32:07 +00:00
{
name : "double-quoted" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 20:32:07 +00:00
vars : {
x : im a var
}
hi : "1 ${x} 2"
` , "" )
assert . Equal ( t , "1 im a var 2" , g . Objects [ 0 ] . Label . Value )
} ,
} ,
2024-03-29 07:40:22 +00:00
{
name : "double-border" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
a . shape : Circle
a . style . double - border : true
` , "" )
} ,
} ,
{
name : "invalid-double-border" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
a . shape : hexagon
a . style . double - border : true
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / basic / invalid - double - border . d2 : 3 : 1 : key "double-border" can only be applied to squares , rectangles , circles , ovals ` )
} ,
} ,
2023-07-11 20:32:07 +00:00
{
name : "single-quoted" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 20:32:07 +00:00
vars : {
x : im a var
}
hi : ' 1 $ { x } 2 '
` , "" )
assert . Equal ( t , "1 ${x} 2" , g . Objects [ 0 ] . Label . Value )
} ,
} ,
2023-07-11 04:18:18 +00:00
{
2023-07-12 00:19:34 +00:00
name : "edge-label" ,
2023-07-11 04:18:18 +00:00
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 04:18:18 +00:00
vars : {
x : im a var
}
a - > b : $ { x }
` , "" )
assert . Equal ( t , 1 , len ( g . Edges ) )
assert . Equal ( t , "im a var" , g . Edges [ 0 ] . Label . Value )
} ,
} ,
2023-07-12 00:33:01 +00:00
{
name : "edge-map" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 00:33:01 +00:00
vars : {
x : im a var
}
a - > b : {
target - arrowhead . label : $ { x }
}
` , "" )
assert . Equal ( t , 1 , len ( g . Edges ) )
assert . Equal ( t , "im a var" , g . Edges [ 0 ] . DstArrowhead . Label . Value )
} ,
} ,
2023-07-11 21:56:09 +00:00
{
name : "quoted-var" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 21:56:09 +00:00
vars : {
primaryColors : {
button : {
active : "#4baae5"
}
}
}
button : {
style : {
border - radius : 5
fill : $ { primaryColors . button . active }
}
}
` , "" )
assert . Equal ( t , ` #4baae5 ` , g . Objects [ 0 ] . Style . Fill . Value )
} ,
} ,
{
name : "quoted-var-quoted-sub" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 21:56:09 +00:00
vars : {
x : "hi"
}
y : "hey ${x}"
` , "" )
2023-07-13 17:13:26 +00:00
assert . Equal ( t , ` hey hi ` , g . Objects [ 0 ] . Label . Value )
2023-07-11 21:56:09 +00:00
} ,
} ,
2023-07-12 00:33:01 +00:00
{
name : "parent-scope" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 00:33:01 +00:00
vars : {
x : im root var
}
a : {
vars : {
b : im nested var
}
hi : $ { x }
}
` , "" )
assert . Equal ( t , "im root var" , g . Objects [ 1 ] . Label . Value )
} ,
} ,
2023-07-12 18:55:58 +00:00
{
name : "map" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 18:55:58 +00:00
vars : {
cool - style : {
fill : red
}
arrows : {
target - arrowhead . label : yay
}
}
hi . style : $ { cool - style }
a - > b : $ { arrows }
` , "" )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Style . Fill . Value )
assert . Equal ( t , "yay" , g . Edges [ 0 ] . DstArrowhead . Label . Value )
} ,
} ,
{
name : "primary-and-composite" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 18:55:58 +00:00
vars : {
x : all {
a : b
}
}
z : $ { x }
` , "" )
2024-09-25 03:41:47 +00:00
assert . Equal ( t , "z" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "all" , g . Objects [ 0 ] . Label . Value )
assert . Equal ( t , 1 , len ( g . Objects [ 0 ] . Children ) )
2023-07-12 18:55:58 +00:00
} ,
} ,
2023-07-12 21:53:28 +00:00
{
name : "spread" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 21:53:28 +00:00
vars : {
x : all {
a : b
b : c
}
}
z : {
... $ { x }
c
}
` , "" )
2024-09-25 03:41:47 +00:00
assert . Equal ( t , "z" , g . Objects [ 0 ] . ID )
2023-07-13 19:24:48 +00:00
assert . Equal ( t , 4 , len ( g . Objects ) )
2024-09-25 03:41:47 +00:00
assert . Equal ( t , 3 , len ( g . Objects [ 0 ] . Children ) )
2023-07-12 21:53:28 +00:00
} ,
} ,
2023-07-13 05:20:55 +00:00
{
name : "array" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 05:20:55 +00:00
vars : {
base - constraints : [ UNQ ; NOT NULL ]
}
a : {
shape : sql_table
b : int { constraint : $ { base - constraints } }
}
` , "" )
assert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
assert . Equal ( t , 2 , len ( g . Objects [ 0 ] . SQLTable . Columns [ 0 ] . Constraint ) )
} ,
} ,
2025-02-25 18:20:39 +00:00
{
name : "comment-array" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
list : [
"a" ;
"b" ;
"c" ;
"d"
# e
]
}
a
` , "" )
} ,
} ,
2023-07-13 05:20:55 +00:00
{
name : "spread-array" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 05:20:55 +00:00
vars : {
base - constraints : [ UNQ ; NOT NULL ]
}
a : {
shape : sql_table
b : int { constraint : [ PK ; ... $ { base - constraints } ] }
}
` , "" )
assert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
assert . Equal ( t , 3 , len ( g . Objects [ 0 ] . SQLTable . Columns [ 0 ] . Constraint ) )
} ,
} ,
{
name : "sub-array" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 05:20:55 +00:00
vars : {
x : all
}
z . class : [ a ; $ { x } ]
` , "" )
assert . Equal ( t , "z" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "all" , g . Objects [ 0 ] . Attributes . Classes [ 1 ] )
} ,
} ,
{
name : "multi-part-array" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 05:20:55 +00:00
vars : {
x : all
}
z . class : [ a ; $ { x } together ]
` , "" )
assert . Equal ( t , "z" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "alltogether" , g . Objects [ 0 ] . Attributes . Classes [ 1 ] )
} ,
} ,
2023-07-13 17:11:08 +00:00
{
name : "double-quote-primary" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 17:11:08 +00:00
vars : {
x : always {
a : b
}
}
z : "${x} be my maybe"
` , "" )
assert . Equal ( t , "z" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "always be my maybe" , g . Objects [ 0 ] . Label . Value )
} ,
} ,
2023-07-13 19:24:48 +00:00
{
name : "spread-nested" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 19:24:48 +00:00
vars : {
disclaimer : {
I am not a lawyer
}
}
custom - disclaimer : DRAFT DISCLAIMER {
... $ { disclaimer }
}
` , "" )
assert . Equal ( t , 2 , len ( g . Objects ) )
} ,
} ,
2023-07-13 21:47:32 +00:00
{
name : "spread-edge" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 21:47:32 +00:00
vars : {
connections : {
x - > a
}
}
hi : {
... $ { connections }
}
` , "" )
assert . Equal ( t , 3 , len ( g . Objects ) )
assert . Equal ( t , 1 , len ( g . Edges ) )
} ,
} ,
2023-07-11 04:18:18 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
t . Run ( "override" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "label" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 04:18:18 +00:00
vars : {
x : im a var
}
hi : $ { x }
hi : not a var
` , "" )
assert . Equal ( t , 1 , len ( g . Objects ) )
assert . Equal ( t , "not a var" , g . Objects [ 0 ] . Label . Value )
} ,
} ,
2023-07-12 00:19:34 +00:00
{
name : "map" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 00:19:34 +00:00
vars : {
x : im root var
}
a : {
vars : {
x : im nested var
}
hi : $ { x }
}
` , "" )
assert . Equal ( t , "im nested var" , g . Objects [ 1 ] . Label . Value )
} ,
} ,
2023-07-12 01:05:03 +00:00
{
name : "var-in-var" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 01:05:03 +00:00
vars : {
surname : Smith
}
a : {
vars : {
trade1 : Black $ { surname }
trade2 : Metal $ { surname }
}
hi : $ { trade1 }
}
` , "" )
assert . Equal ( t , "BlackSmith" , g . Objects [ 1 ] . Label . Value )
} ,
} ,
2023-07-12 06:26:06 +00:00
{
name : "recursive-var" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-12 06:26:06 +00:00
vars : {
x : a
}
hi : {
vars : {
x : $ { x } - b
2024-08-06 02:18:34 +00:00
b : $ { x }
2023-07-12 06:26:06 +00:00
}
yo : $ { x }
2024-08-06 02:18:34 +00:00
hey : $ { b }
2023-07-12 06:26:06 +00:00
}
` , "" )
assert . Equal ( t , "a-b" , g . Objects [ 1 ] . Label . Value )
2024-08-06 02:18:34 +00:00
assert . Equal ( t , "a-b" , g . Objects [ 2 ] . Label . Value )
2023-07-12 06:26:06 +00:00
} ,
} ,
2023-07-13 17:22:37 +00:00
{
name : "null" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
surname : Smith
}
a : {
vars : {
surname : null
}
hi : John $ { surname }
}
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / override / null . d2 : 9 : 3 : could not resolve variable "surname" ` )
} ,
} ,
{
name : "nested-null" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
surnames : {
john : smith
}
}
a : {
vars : {
surnames : {
john : null
}
}
hi : John $ { surname }
}
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / override / nested - null . d2 : 13 : 3 : could not resolve variable "surname" ` )
} ,
} ,
2023-07-11 02:24:21 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
2023-07-11 04:32:30 +00:00
t . Run ( "boards" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "layer" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 04:32:30 +00:00
vars : {
x : im a var
}
layers : {
l : {
hi : $ { x }
}
}
` , "" )
assert . Equal ( t , 1 , len ( g . Layers [ 0 ] . Objects ) )
assert . Equal ( t , "im a var" , g . Layers [ 0 ] . Objects [ 0 ] . Label . Value )
} ,
} ,
2023-07-13 06:01:11 +00:00
{
name : "layer-2" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-13 06:01:11 +00:00
vars : {
x : root var x
y : root var y
}
layers : {
l : {
vars : {
x : layer var x
}
hi : $ { x }
hello : $ { y }
}
}
` , "" )
assert . Equal ( t , "hi" , g . Layers [ 0 ] . Objects [ 0 ] . ID )
assert . Equal ( t , "layer var x" , g . Layers [ 0 ] . Objects [ 0 ] . Label . Value )
assert . Equal ( t , "hello" , g . Layers [ 0 ] . Objects [ 1 ] . ID )
assert . Equal ( t , "root var y" , g . Layers [ 0 ] . Objects [ 1 ] . Label . Value )
} ,
} ,
2023-07-11 04:54:34 +00:00
{
name : "scenario" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 04:54:34 +00:00
vars : {
x : im a var
}
scenarios : {
l : {
hi : $ { x }
}
}
` , "" )
assert . Equal ( t , 1 , len ( g . Scenarios [ 0 ] . Objects ) )
assert . Equal ( t , "im a var" , g . Scenarios [ 0 ] . Objects [ 0 ] . Label . Value )
} ,
} ,
{
name : "overlay" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 04:54:34 +00:00
vars : {
x : im x var
}
scenarios : {
l : {
vars : {
y : im y var
}
x : $ { x }
y : $ { y }
}
}
layers : {
l2 : {
vars : {
y : im y var
}
x : $ { x }
y : $ { y }
}
}
` , "" )
assert . Equal ( t , 2 , len ( g . Scenarios [ 0 ] . Objects ) )
assert . Equal ( t , "im x var" , g . Scenarios [ 0 ] . Objects [ 0 ] . Label . Value )
assert . Equal ( t , "im y var" , g . Scenarios [ 0 ] . Objects [ 1 ] . Label . Value )
assert . Equal ( t , 2 , len ( g . Layers [ 0 ] . Objects ) )
assert . Equal ( t , "im x var" , g . Layers [ 0 ] . Objects [ 0 ] . Label . Value )
assert . Equal ( t , "im y var" , g . Layers [ 0 ] . Objects [ 1 ] . Label . Value )
} ,
} ,
2023-07-11 04:55:49 +00:00
{
name : "replace" ,
run : func ( t * testing . T ) {
2023-07-14 20:08:26 +00:00
g , _ := assertCompile ( t , `
2023-07-11 04:55:49 +00:00
vars : {
x : im x var
}
scenarios : {
l : {
vars : {
x : im replaced x var
}
x : $ { x }
}
}
` , "" )
assert . Equal ( t , 1 , len ( g . Scenarios [ 0 ] . Objects ) )
assert . Equal ( t , "im replaced x var" , g . Scenarios [ 0 ] . Objects [ 0 ] . Label . Value )
} ,
} ,
2023-07-11 04:32:30 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
2023-07-14 20:08:26 +00:00
t . Run ( "config" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "basic" ,
run : func ( t * testing . T ) {
_ , config := assertCompile ( t , `
vars : {
d2 - config : {
sketch : true
}
}
x - > y
` , "" )
assert . Equal ( t , true , * config . Sketch )
} ,
} ,
{
name : "invalid" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
d2 - config : {
sketch : lol
}
}
x - > y
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / config / invalid . d2 : 4 : 5 : expected a boolean for "sketch" , got "lol" ` )
} ,
} ,
{
name : "not-root" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
x : {
vars : {
d2 - config : {
sketch : false
}
}
}
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / config / not - root . d2 : 4 : 4 : "d2-config" can only appear at root vars ` )
} ,
} ,
2024-10-15 18:56:39 +00:00
{
name : "data" ,
run : func ( t * testing . T ) {
_ , config := assertCompile ( t , `
vars : {
d2 - config : {
data : {
cat : hat
2024-10-15 22:37:49 +00:00
later : [ 1 ; 5 ; 2 ]
2024-10-15 18:56:39 +00:00
}
}
}
` , ` ` )
2024-10-15 22:37:49 +00:00
assert . Equal ( t , 2 , len ( config . Data ) )
2024-10-15 18:56:39 +00:00
assert . Equal ( t , "hat" , config . Data [ "cat" ] )
2024-10-15 22:37:49 +00:00
assert . Equal ( t , "1" , ( config . Data [ "later" ] ) . ( [ ] interface { } ) [ 0 ] )
2024-10-15 18:56:39 +00:00
} ,
} ,
2023-07-14 20:08:26 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
2023-07-11 02:24:21 +00:00
t . Run ( "errors" , func ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
name : "missing" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : hey
}
hi : $ { z }
2023-07-11 20:40:44 +00:00
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / missing . d2 : 5 : 1 : could not resolve variable "z" ` )
} ,
} ,
2023-07-12 18:55:58 +00:00
{
name : "multi-part-map" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : {
a : b
}
}
hi : 1 $ { x }
2023-07-13 21:11:14 +00:00
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / multi - part - map . d2 : 7 : 1 : cannot substitute composite variable "x" as part of a string ` )
2023-07-12 18:55:58 +00:00
} ,
} ,
{
name : "quoted-map" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : {
a : b
}
}
hi : "${x}"
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / quoted - map . d2 : 7 : 1 : cannot substitute map variable "x" in quotes ` )
} ,
} ,
2023-07-11 21:01:35 +00:00
{
name : "nested-missing" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : {
y : hey
}
}
hi : $ { x . z }
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / nested - missing . d2 : 7 : 1 : could not resolve variable "x.z" ` )
} ,
} ,
2023-07-12 00:33:01 +00:00
{
name : "out-of-scope" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
a : {
vars : {
x : hey
}
}
hi : $ { x }
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / out - of - scope . d2 : 7 : 1 : could not resolve variable "x" ` )
} ,
} ,
2023-07-12 06:26:06 +00:00
{
name : "recursive-var" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : $ { x }
}
hi : $ { x }
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / recursive - var . d2 : 3 : 3 : could not resolve variable "x" ` )
} ,
} ,
2023-07-13 21:47:32 +00:00
2023-07-12 21:53:28 +00:00
{
name : "spread-non-map" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : all
}
z : {
... $ { x }
c
}
2023-07-13 15:37:22 +00:00
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / spread - non - map . d2 : 6 : 3 : cannot spread non - composite ` )
2023-07-13 05:20:55 +00:00
} ,
} ,
{
name : "missing-array" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : b
}
z : {
class : [ ... $ { a } ]
}
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / missing - array . d2 : 6 : 3 : could not resolve variable "a" ` )
} ,
} ,
{
name : "spread-non-array" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : {
a : b
}
}
z : {
class : [ ... $ { x } ]
}
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / spread - non - array . d2 : 8 : 11 : cannot spread non - array into array ` )
2023-07-12 21:53:28 +00:00
} ,
} ,
{
name : "spread-non-solo" ,
// NOTE: this doesn't get parsed correctly and so the error message isn't exactly right, but the important thing is that it errors
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
x : {
a : b
}
}
z : {
d : ... $ { x }
c
}
2023-07-13 21:11:14 +00:00
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / spread - non - solo . d2 : 8 : 2 : cannot substitute composite variable "x" as part of a string ` )
2023-07-12 21:53:28 +00:00
} ,
} ,
2023-09-22 21:16:35 +00:00
{
name : "spread-mid-string" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
vars : {
test : hello
}
mybox : {
label : prefix $ { test } suffix
}
` , "" )
} ,
} ,
2023-11-01 19:05:45 +00:00
{
name : "undeclared-var-usage" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
x : { ... $ { v } }
` , ` d2 / testdata / d2compiler / TestCompile2 / vars / errors / undeclared - var - usage . d2 : 2 : 4 : could not resolve variable "v" ` )
} ,
} ,
{
name : "split-var-usage" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
x1
vars : {
v : {
style . fill : green
}
}
x1 : { ... $ { v } }
` , ` ` )
} ,
} ,
2023-07-11 02:24:21 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
} )
}
2023-08-16 06:33:50 +00:00
func testGlobs ( t * testing . T ) {
t . Parallel ( )
tca := [ ] struct {
name string
skip bool
run func ( t * testing . T )
} {
{
2023-08-17 02:17:41 +00:00
name : "alixander-lazy-globs-review/1" ,
2023-08-16 06:33:50 +00:00
run : func ( t * testing . T ) {
assertCompile ( t , `
* * * . style . fill : yellow
* * . shape : circle
* . style . multiple : true
x : {
y
}
layers : {
next : {
a
}
}
2023-08-17 02:17:41 +00:00
` , "" )
} ,
} ,
{
name : "alixander-lazy-globs-review/2" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
* * . style . fill : yellow
scenarios : {
b : {
a - > b
}
}
2023-08-20 04:20:32 +00:00
` , "" )
} ,
} ,
{
name : "alixander-lazy-globs-review/3" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
* * * : {
c : d
}
* * * : {
style . fill : red
}
table : {
shape : sql_table
a : b
}
class : {
shape : class
a : b
}
2023-08-16 06:33:50 +00:00
` , "" )
} ,
} ,
2023-10-31 19:26:01 +00:00
{
name : "double-glob-err-val" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
* * : {
label : hi
label . near : center
}
x : {
a - > b
}
` , ` d2 / testdata / d2compiler / TestCompile2 / globs / double - glob - err - val . d2 : 4 : 3 : invalid "near" field ` )
} ,
} ,
{
name : "double-glob-override-err-val" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
( * * - > * * ) [ * ] : {
label . near : top - center
}
( * * - > * * ) [ * ] : {
label . near : invalid
}
x : {
a - > b
}
` , ` d2 / testdata / d2compiler / TestCompile2 / globs / double - glob - override - err - val . d2 : 6 : 2 : invalid "near" field ` )
} ,
} ,
2023-11-08 22:56:46 +00:00
{
name : "creating-node-bug" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* . * a - > * . * b
container_1 : {
a
}
container_2 : {
b
}
` , ` ` )
assert . Equal ( t , 4 , len ( g . Objects ) )
} ,
} ,
2024-03-05 01:51:05 +00:00
{
name : "override-edge/1" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
( * - > * ) [ * ] . style . stroke : red
( * - > * ) [ * ] . style . stroke : green
a - > b
` , ` ` )
assert . Equal ( t , "green" , g . Edges [ 0 ] . Attributes . Style . Stroke . Value )
} ,
} ,
{
name : "override-edge/2" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
( * - > * ) [ * ] . style . stroke : red
a - > b : { style . stroke : green }
a - > b
` , ` ` )
assert . Equal ( t , "green" , g . Edges [ 0 ] . Attributes . Style . Stroke . Value )
assert . Equal ( t , "red" , g . Edges [ 1 ] . Attributes . Style . Stroke . Value )
} ,
} ,
2024-07-18 22:05:49 +00:00
{
name : "exists-filter" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& link : *
style . underline : true
}
2024-07-18 22:18:41 +00:00
x
y . link : https : //google.com
` , ` ` )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Attributes . Style . Underline )
assert . Equal ( t , "true" , g . Objects [ 1 ] . Attributes . Style . Underline . Value )
} ,
} ,
2025-01-20 22:54:07 +00:00
{
2025-02-05 00:32:45 +00:00
name : "leaf-filter-1" ,
2025-01-20 22:54:07 +00:00
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
2025-01-21 02:47:26 +00:00
* * : {
2025-01-20 22:54:07 +00:00
& leaf : false
2025-01-21 02:47:26 +00:00
style . fill : red
2025-01-20 22:54:07 +00:00
}
2025-02-05 00:32:45 +00:00
* * : {
& leaf : true
style . stroke : yellow
}
2025-01-20 22:54:07 +00:00
a . b . c
2025-01-21 02:55:23 +00:00
` , ` ` )
assert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Attributes . Style . Fill . Value )
2025-02-05 00:32:45 +00:00
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Attributes . Style . Stroke )
2025-01-21 02:55:23 +00:00
assert . Equal ( t , "b" , g . Objects [ 1 ] . ID )
assert . Equal ( t , "red" , g . Objects [ 1 ] . Attributes . Style . Fill . Value )
2025-02-05 00:32:45 +00:00
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 1 ] . Attributes . Style . Stroke )
2025-01-21 02:55:23 +00:00
assert . Equal ( t , "c" , g . Objects [ 2 ] . ID )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 2 ] . Attributes . Style . Fill )
2025-02-05 00:32:45 +00:00
assert . Equal ( t , "yellow" , g . Objects [ 2 ] . Attributes . Style . Stroke . Value )
} ,
} ,
{
name : "leaf-filter-2" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* * : {
& leaf : true
style . stroke : yellow
}
a : {
b - > c
}
d : {
e
}
` , ` ` )
assert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Attributes . Style . Stroke )
assert . Equal ( t , "b" , g . Objects [ 1 ] . ID )
assert . Equal ( t , "yellow" , g . Objects [ 1 ] . Attributes . Style . Stroke . Value )
assert . Equal ( t , "c" , g . Objects [ 2 ] . ID )
assert . Equal ( t , "yellow" , g . Objects [ 2 ] . Attributes . Style . Stroke . Value )
assert . Equal ( t , "d" , g . Objects [ 3 ] . ID )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 3 ] . Attributes . Style . Stroke )
assert . Equal ( t , "e" , g . Objects [ 4 ] . ID )
assert . Equal ( t , "yellow" , g . Objects [ 4 ] . Attributes . Style . Stroke . Value )
2025-01-21 02:55:23 +00:00
} ,
} ,
2025-03-29 23:31:51 +00:00
{
name : "level-filter" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* * : {
& level : 0
style . fill : red
}
* * : {
& level : 1
style . stroke : yellow
}
2025-03-30 14:33:31 +00:00
( * * - > * * ) [ * ] : {
& src . level : 0
& dst . level : 0
style . stroke : blue
}
2025-03-29 23:31:51 +00:00
a . b . c
2025-03-30 14:33:31 +00:00
x - > y
a : {
1 - > 2
}
a .1 - > x
2025-03-29 23:31:51 +00:00
` , ` ` )
assert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Attributes . Style . Fill . Value )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Attributes . Style . Stroke )
assert . Equal ( t , "b" , g . Objects [ 1 ] . ID )
assert . Equal ( t , "yellow" , g . Objects [ 1 ] . Attributes . Style . Stroke . Value )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 1 ] . Attributes . Style . Fill )
assert . Equal ( t , "c" , g . Objects [ 2 ] . ID )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 2 ] . Attributes . Style . Fill )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 2 ] . Attributes . Style . Stroke )
2025-03-30 14:33:31 +00:00
assert . Equal ( t , "(x -> y)[0]" , g . Edges [ 0 ] . AbsID ( ) )
2025-03-31 17:49:07 +00:00
assert . Equal ( t , "blue" , g . Edges [ 0 ] . Attributes . Style . Stroke . Value )
assert . Equal ( t , "a.(1 -> 2)[0]" , g . Edges [ 1 ] . AbsID ( ) )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 1 ] . Attributes . Style . Stroke )
assert . Equal ( t , "(a.1 -> x)[0]" , g . Edges [ 2 ] . AbsID ( ) )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 2 ] . Attributes . Style . Stroke )
2025-03-29 23:31:51 +00:00
} ,
} ,
2025-01-21 02:55:23 +00:00
{
name : "connected-filter" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& connected : true
style . fill : red
}
a - > b
c
2025-01-20 22:54:07 +00:00
` , ` ` )
assert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Attributes . Style . Fill . Value )
2025-01-21 02:47:26 +00:00
assert . Equal ( t , "b" , g . Objects [ 1 ] . ID )
2025-01-20 22:54:07 +00:00
assert . Equal ( t , "red" , g . Objects [ 1 ] . Attributes . Style . Fill . Value )
2025-01-21 02:47:26 +00:00
assert . Equal ( t , "c" , g . Objects [ 2 ] . ID )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 2 ] . Attributes . Style . Fill )
2025-01-20 22:54:07 +00:00
} ,
} ,
2025-03-30 14:08:10 +00:00
{
name : "and-filter" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& shape : person
& connected : true
style . fill : red
}
( * * - > * * ) [ * ] : {
& src : a
& dst : c
style . stroke : yellow
}
a - > b
a . shape : person
a - > c
` , ` ` )
assert . Equal ( t , "a" , g . Objects [ 0 ] . ID )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Attributes . Style . Fill . Value )
assert . Equal ( t , "b" , g . Objects [ 1 ] . ID )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 1 ] . Attributes . Style . Fill )
assert . Equal ( t , "c" , g . Objects [ 2 ] . ID )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 2 ] . Attributes . Style . Fill )
assert . Equal ( t , "(a -> b)[0]" , g . Edges [ 0 ] . AbsID ( ) )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 0 ] . Attributes . Style . Stroke )
assert . Equal ( t , "(a -> c)[0]" , g . Edges [ 1 ] . AbsID ( ) )
assert . Equal ( t , "yellow" , g . Edges [ 1 ] . Attributes . Style . Stroke . Value )
} ,
} ,
2024-07-18 22:18:41 +00:00
{
name : "glob-filter" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& link : * google *
style . underline : true
}
2024-07-18 22:05:49 +00:00
x
y . link : https : //google.com
z . link : https : //yahoo.com
` , ` ` )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 0 ] . Attributes . Style . Underline )
assert . Equal ( t , "true" , g . Objects [ 1 ] . Attributes . Style . Underline . Value )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 2 ] . Attributes . Style . Underline )
} ,
} ,
2024-07-26 00:36:25 +00:00
{
2024-07-27 16:01:21 +00:00
name : "reapply-scenario" ,
2024-07-26 00:36:25 +00:00
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
2024-07-27 16:01:21 +00:00
* . b * . shape : circle
x : {
b
}
scenarios : {
k : {
x : {
b
}
}
}
` , ` ` )
assert . Equal ( t , "circle" , g . Objects [ 1 ] . Attributes . Shape . Value )
assert . Equal ( t , "circle" , g . Scenarios [ 0 ] . Objects [ 1 ] . Attributes . Shape . Value )
} ,
} ,
{
name : "second-scenario" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* . b * . shape : circle
2024-07-26 00:36:25 +00:00
scenarios : {
k : {
x : {
b
}
}
z : {
x : {
b
}
}
}
` , ` ` )
assert . Equal ( t , "circle" , g . Scenarios [ 0 ] . Objects [ 1 ] . Attributes . Shape . Value )
assert . Equal ( t , "circle" , g . Scenarios [ 1 ] . Objects [ 1 ] . Attributes . Shape . Value )
} ,
} ,
2024-08-06 18:11:04 +00:00
{
name : "default-glob-filter/1" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& shape : rectangle
style . fill : red
}
* : {
& style . opacity : 1
style . stroke : blue
}
a
b . shape : circle
c . shape : rectangle
` , ` ` )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Style . Fill . Value )
assert . Equal ( t , "blue" , g . Objects [ 0 ] . Style . Stroke . Value )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 1 ] . Style . Fill )
assert . Equal ( t , "red" , g . Objects [ 2 ] . Style . Fill . Value )
} ,
} ,
{
name : "default-glob-filter/2" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& shape : rectangle
style . opacity : 0.2
}
a
b - > c
` , ` ` )
assert . Equal ( t , "0.2" , g . Objects [ 0 ] . Style . Opacity . Value )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 0 ] . Style . Opacity )
} ,
} ,
{
name : "default-glob-filter/3" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& icon : ""
style . opacity : 0.2
}
a
b . icon : https : //google.com/cat.jpg
` , ` ` )
assert . Equal ( t , "0.2" , g . Objects [ 0 ] . Style . Opacity . Value )
assert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Objects [ 1 ] . Style . Opacity )
} ,
} ,
{
name : "default-glob-filter/4" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
* : {
& opacity : 1
style . stroke : red
}
( * - > * ) [ * ] : {
& opacity : 1
style . stroke : red
}
a
b - > c
` , ` ` )
assert . Equal ( t , "red" , g . Objects [ 0 ] . Style . Stroke . Value )
assert . Equal ( t , "red" , g . Edges [ 0 ] . Style . Stroke . Value )
} ,
} ,
2025-03-04 03:33:37 +00:00
{
name : "merge-glob-values" ,
run : func ( t * testing . T ) {
assertCompile ( t , `
"a"
* . style . stroke - width : 2
* . style . font - size : 14
a . width : 339
` , ` ` )
} ,
} ,
2025-02-28 00:28:17 +00:00
{
name : "mixed-edge-quoting" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
"a" . "b" . "c" . "z1" - > "a" . "b" . "c" . "z2"
` , ` ` )
assert . Equal ( t , 5 , len ( g . Objects ) )
} ,
} ,
2025-02-28 18:17:18 +00:00
{
2025-02-28 22:28:58 +00:00
name : "suspension-lazy" ,
2025-02-28 18:17:18 +00:00
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a - > b
c
* * : suspend
( * * - > * * ) [ * ] : suspend
d
` , ` ` )
2025-02-28 22:28:58 +00:00
assert . Equal ( t , 1 , len ( g . Objects ) )
} ,
} ,
{
name : "suspension-quotes" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a - > b
c
* * : suspend
( * * - > * * ) [ * ] : suspend
d : "suspend"
d - > d : "suspend"
` , ` ` )
assert . Equal ( t , 1 , len ( g . Objects ) )
assert . Equal ( t , 1 , len ( g . Edges ) )
2025-02-28 18:17:18 +00:00
} ,
} ,
2025-03-12 20:56:09 +00:00
{
name : "unsuspend-edge-label" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a - > b : hello
c
* * : suspend
( * * - > * * ) [ * ] : suspend
( * - > * ) [ * ] : unsuspend
` , ` ` )
assert . Equal ( t , 2 , len ( g . Objects ) )
assert . Equal ( t , 1 , len ( g . Edges ) )
assert . Equal ( t , "hello" , g . Edges [ 0 ] . Label . Value )
} ,
} ,
2025-03-12 21:29:48 +00:00
{
name : "unsuspend-edge-filter" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a - > b
* * : suspend
( * * - > * * ) [ * ] : suspend
( * - > * ) [ * ] : unsuspend {
& dst : a
}
` , ` ` )
assert . Equal ( t , 0 , len ( g . Objects ) )
assert . Equal ( t , 0 , len ( g . Edges ) )
} ,
} ,
2025-03-12 22:05:08 +00:00
{
name : "unsuspend-edge-child" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a : {
b - > c
}
* * : suspend
( * * - > * * ) [ * ] : suspend
( * * - > * * ) [ * ] : unsuspend {
& dst : a . c
}
` , ` ` )
assert . Equal ( t , 3 , len ( g . Objects ) )
assert . Equal ( t , 1 , len ( g . Edges ) )
} ,
} ,
2025-03-12 22:22:20 +00:00
{
name : "unsuspend-cross-container-edge-label" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a : {
b
}
c : {
d
}
a . b - > c . d : likes
* * : suspend
( * * - > * * ) [ * ] : suspend
( * * - > * * ) [ * ] : unsuspend {
& label : likes
}
` , ` ` )
assert . Equal ( t , 4 , len ( g . Objects ) )
assert . Equal ( t , 1 , len ( g . Edges ) )
} ,
} ,
2025-03-12 20:56:09 +00:00
{
name : "unsuspend-shape-label" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a : hello
* : suspend
* : unsuspend
` , ` ` )
assert . Equal ( t , 1 , len ( g . Objects ) )
assert . Equal ( t , "hello" , g . Objects [ 0 ] . Label . Value )
} ,
} ,
{
name : "suspend-shape" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a : hello
* : suspend
` , ` ` )
assert . Equal ( t , 0 , len ( g . Objects ) )
} ,
} ,
2025-03-01 16:58:52 +00:00
{
name : "edge-glob-ampersand-filter/1" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
( * - > * ) [ * ] : {
& src : a
style . stroke - dash : 3
}
( * - > * ) [ * ] : {
& dst : c
style . stroke : blue
}
( * - > * ) [ * ] : {
& src : b
& dst : c
style . fill : red
}
a - > b
b - > c
a - > c
` , ` ` )
tassert . Equal ( t , 3 , len ( g . Edges ) )
tassert . Equal ( t , "a" , g . Edges [ 0 ] . Src . ID )
tassert . Equal ( t , "b" , g . Edges [ 0 ] . Dst . ID )
tassert . Equal ( t , "3" , g . Edges [ 0 ] . Style . StrokeDash . Value )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 0 ] . Style . Stroke )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 0 ] . Style . Fill )
tassert . Equal ( t , "b" , g . Edges [ 1 ] . Src . ID )
tassert . Equal ( t , "c" , g . Edges [ 1 ] . Dst . ID )
tassert . Equal ( t , "blue" , g . Edges [ 1 ] . Style . Stroke . Value )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 1 ] . Style . StrokeDash )
tassert . Equal ( t , "red" , g . Edges [ 1 ] . Style . Fill . Value )
tassert . Equal ( t , "a" , g . Edges [ 2 ] . Src . ID )
tassert . Equal ( t , "c" , g . Edges [ 2 ] . Dst . ID )
tassert . Equal ( t , "3" , g . Edges [ 2 ] . Style . StrokeDash . Value )
tassert . Equal ( t , "blue" , g . Edges [ 2 ] . Style . Stroke . Value )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 2 ] . Style . Fill )
} ,
} ,
{
name : "edge-glob-ampersand-filter/2" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a : {
shape : circle
style : {
fill : blue
opacity : 0.8
}
}
b : {
shape : rectangle
style : {
fill : red
opacity : 0.5
}
}
c : {
shape : diamond
style . fill : green
style . opacity : 0.8
}
( * - > * ) [ * ] : {
& src . style . fill : blue
style . stroke - dash : 3
}
( * - > * ) [ * ] : {
& dst . style . opacity : 0.8
style . stroke : cyan
}
( * - > * ) [ * ] : {
& src . shape : rectangle
& dst . style . fill : green
style . stroke - width : 5
}
a - > b
b - > c
a - > c
` , ` ` )
tassert . Equal ( t , 3 , len ( g . Edges ) )
tassert . Equal ( t , "a" , g . Edges [ 0 ] . Src . ID )
tassert . Equal ( t , "b" , g . Edges [ 0 ] . Dst . ID )
tassert . Equal ( t , "3" , g . Edges [ 0 ] . Style . StrokeDash . Value )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 0 ] . Style . Stroke )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 0 ] . Style . StrokeWidth )
tassert . Equal ( t , "b" , g . Edges [ 1 ] . Src . ID )
tassert . Equal ( t , "c" , g . Edges [ 1 ] . Dst . ID )
tassert . Equal ( t , "cyan" , g . Edges [ 1 ] . Style . Stroke . Value )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 1 ] . Style . StrokeDash )
tassert . Equal ( t , "5" , g . Edges [ 1 ] . Style . StrokeWidth . Value )
tassert . Equal ( t , "a" , g . Edges [ 2 ] . Src . ID )
tassert . Equal ( t , "c" , g . Edges [ 2 ] . Dst . ID )
tassert . Equal ( t , "3" , g . Edges [ 2 ] . Style . StrokeDash . Value )
tassert . Equal ( t , "cyan" , g . Edges [ 2 ] . Style . Stroke . Value )
tassert . Equal ( t , ( * d2graph . Scalar ) ( nil ) , g . Edges [ 2 ] . Style . StrokeWidth )
} ,
} ,
2025-03-02 20:26:58 +00:00
{
name : "md-shape" ,
run : func ( t * testing . T ) {
g , _ := assertCompile ( t , `
a . shape : circle
a : | md # hi |
b . shape : circle
b . label : | md # hi |
c : | md # hi |
c . shape : circle
d . label : | md # hi |
d . shape : circle
e : {
shape : circle
label : | md # hi |
}
` , ` ` )
tassert . Equal ( t , 5 , len ( g . Objects ) )
for _ , obj := range g . Objects {
tassert . Equal ( t , "circle" , obj . Shape . Value , "Object " + obj . ID + " should have circle shape" )
tassert . Equal ( t , "markdown" , obj . Language , "Object " + obj . ID + " should have md language" )
}
} ,
} ,
2023-08-16 06:33:50 +00:00
}
for _ , tc := range tca {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
t . Parallel ( )
if tc . skip {
t . SkipNow ( )
}
tc . run ( t )
} )
}
}
2023-07-14 20:08:26 +00:00
func assertCompile ( t * testing . T , text string , expErr string ) ( * d2graph . Graph , * d2target . Config ) {
2023-01-04 01:50:27 +00:00
d2Path := fmt . Sprintf ( "d2/testdata/d2compiler/%v.d2" , t . Name ( ) )
2023-07-14 20:08:26 +00:00
g , config , err := d2compiler . Compile ( d2Path , strings . NewReader ( text ) , nil )
2023-01-04 01:50:27 +00:00
if expErr != "" {
assert . Error ( t , err )
assert . ErrorString ( t , err , expErr )
} else {
assert . Success ( t , err )
}
got := struct {
Graph * d2graph . Graph ` json:"graph" `
Err error ` json:"err" `
} {
Graph : g ,
Err : err ,
}
err = diff . TestdataJSON ( filepath . Join ( ".." , "testdata" , "d2compiler" , t . Name ( ) ) , got )
assert . Success ( t , err )
2023-07-14 20:08:26 +00:00
return g , config
2023-01-04 01:50:27 +00:00
}