package modules import ( "html/template" "strings" "testing" ) // lines builds the Line slice annotateStructure expects, mirroring what // blobMod produces before the structure pass runs. func lines(raw string) []Line { out := []Line{} for i, t := range strings.Split(raw, "\n") { out = append(out, Line{N: i + 1, Text: template.HTML(t)}) } return out } const sampleYAML = `defaults: &defaults adapter: postgres pool: 5 development: <<: *defaults database: dev test: <<: *defaults database: test hosts: - a - b ` func TestFoldRangesYAML(t *testing.T) { ls := lines(sampleYAML) annotateStructure(ls, []byte(sampleYAML), "yaml") want := map[int]int{ 1: 3, // defaults: block ends at "pool: 5", not the blank line after 5: 7, // development: 9: 14, // test:, stopping short of the empty line 15 12: 14, // hosts: } for _, l := range ls { if got, ok := want[l.N]; ok { if l.FoldTo != got { t.Errorf("line %d: FoldTo = %d, want %d", l.N, l.FoldTo, got) } } else if l.FoldTo != 0 { t.Errorf("line %d: FoldTo = %d, want none", l.N, l.FoldTo) } } if ls[1].Depth != 1 { t.Errorf("line 2 depth = %d, want 1", ls[1].Depth) } if ls[13].Depth != 2 { t.Errorf("line 14 depth = %d, want 2", ls[13].Depth) } } func TestKeysAndAnchors(t *testing.T) { ls := lines(sampleYAML) annotateStructure(ls, []byte(sampleYAML), "yaml") if ls[0].Key != "defaults" { t.Errorf("line 1 key = %q, want %q", ls[0].Key, "defaults") } if ls[13].Key != "" { t.Errorf("line 14 (sequence item) key = %q, want none", ls[13].Key) } // The anchor is reported on the line the &token is written on, not on // the line the mapping it names begins. a := ls[0].Anchor if a == nil { t.Fatal("line 1: no anchor") } if a.Name != "defaults" || a.Uses != 2 { t.Errorf("anchor = %+v, want {defaults 2}", *a) } for _, n := range []int{6, 10} { r := ls[n-1].Ref if r == nil { t.Fatalf("line %d: no ref", n) } if r.Name != "defaults" || r.Line != 1 { t.Errorf("line %d ref = %+v, want defaults@1", n, *r) } if !strings.Contains(r.Preview, "adapter: postgres") { t.Errorf("line %d preview = %q, want the resolved mapping", n, r.Preview) } } } // Anchors defined in the second document of a stream are still resolved: // yaml.Unmarshal would only ever see the first. func TestMultiDocumentAnchors(t *testing.T) { const src = `--- kind: First --- base: &b x: 1 copy: *b ` ls := lines(src) annotateStructure(ls, []byte(src), "yaml") if ls[3].Anchor == nil || ls[3].Anchor.Name != "b" { t.Fatalf("line 4 anchor = %+v, want b", ls[3].Anchor) } if r := ls[5].Ref; r == nil || r.Line != 4 { t.Fatalf("line 6 ref = %+v, want b@4", r) } } func TestFoldRangesJSON(t *testing.T) { const src = `{ "name": "gsg", "deps": { "a": 1 }, "list": [ 2 ] }` ls := lines(src) annotateStructure(ls, []byte(src), "json") if ls[0].FoldTo != 8 { t.Errorf("line 1 FoldTo = %d, want 8", ls[0].FoldTo) } if ls[2].FoldTo != 4 { t.Errorf("line 3 FoldTo = %d, want 4", ls[2].FoldTo) } if ls[5].FoldTo != 7 { t.Errorf("line 6 FoldTo = %d, want 7", ls[5].FoldTo) } if ls[1].Key != "name" { t.Errorf("line 2 key = %q, want %q", ls[1].Key, "name") } // No anchors outside YAML, and a brace inside a string is not structure. if ls[1].Anchor != nil || ls[1].Ref != nil { t.Error("JSON line carries anchor/ref data") } } // Minified JSON has no indentation to fold on; it must degrade to a plain // view rather than producing bogus ranges. func TestMinifiedJSONHasNoFolds(t *testing.T) { const src = `{"a":{"b":[1,2]},"c":3}` ls := lines(src) annotateStructure(ls, []byte(src), "json") if ls[0].FoldTo != 0 { t.Errorf("FoldTo = %d, want 0", ls[0].FoldTo) } }