diff --git a/internal/godoc/codec/generate_test.go b/internal/godoc/codec/generate_test.go index 37b1b3bd2..23df54813 100644 --- a/internal/godoc/codec/generate_test.go +++ b/internal/godoc/codec/generate_test.go @@ -10,12 +10,11 @@ import ( "go/ast" "go/token" "io" - "io/ioutil" - "path/filepath" "reflect" "testing" "github.com/google/go-cmp/cmp" + "golang.org/x/pkgsite/internal/testing/testhelper" ) var update = flag.Bool("update", false, "update goldens instead of checking against them") @@ -57,30 +56,7 @@ func testGenerate(t *testing.T, name string, x interface{}) { t.Fatal(err) } got := buf.String() - if *update { - writeGolden(t, name, got) - } else { - want := readGolden(t, name) - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("%s: mismatch (-want, +got):\n%s", name, diff) - } - } -} - -func writeGolden(t *testing.T, name string, data string) { - filename := filepath.Join("testdata", name+".go") - if err := ioutil.WriteFile(filename, []byte(data), 0644); err != nil { - t.Fatal(err) - } - t.Logf("wrote %s", filename) -} - -func readGolden(t *testing.T, name string) string { - data, err := ioutil.ReadFile(filepath.Join("testdata", name+".go")) - if err != nil { - t.Fatal(err) - } - return string(data) + testhelper.CompareWithGolden(t, got, name+".go", *update) } func TestExportedFields(t *testing.T) { diff --git a/internal/godoc/dochtml/dochtml_test.go b/internal/godoc/dochtml/dochtml_test.go index 991e68a4b..c38a2f458 100644 --- a/internal/godoc/dochtml/dochtml_test.go +++ b/internal/godoc/dochtml/dochtml_test.go @@ -7,6 +7,9 @@ package dochtml import ( "bytes" "context" + "flag" + "fmt" + "go/ast" "go/parser" "go/token" @@ -22,10 +25,13 @@ import ( "golang.org/x/pkgsite/internal/godoc/dochtml/internal/render" "golang.org/x/pkgsite/internal/godoc/internal/doc" "golang.org/x/pkgsite/internal/testing/htmlcheck" + "golang.org/x/pkgsite/internal/testing/testhelper" ) var templateSource = template.TrustedSourceFromConstant("../../../content/static/html/doc") +var update = flag.Bool("update", false, "update goldens instead of checking against them") + var ( in = htmlcheck.In hasAttr = htmlcheck.HasAttr @@ -41,27 +47,28 @@ var testRenderOptions = RenderOptions{ } func TestRenderParts(t *testing.T) { + ctx := context.Background() LoadTemplates(templateSource) - fset, d := mustLoadPackage("everydecl") - ctx := context.Background() + fset, d := mustLoadPackage("everydecl") parts, err := Render(ctx, fset, d, testRenderOptions) if err != nil { t.Fatal(err) } - bodyDoc, err := html.Parse(strings.NewReader(parts.Body.String())) - if err != nil { - t.Fatal(err) - } + compareWithGolden(t, parts, "everydecl", *update) - sidenavDoc, err := html.Parse(strings.NewReader(parts.Outline.String())) + fset2, d2 := mustLoadPackage("deprecated") + parts2, err := Render(ctx, fset2, d2, testRenderOptions) if err != nil { t.Fatal(err) } - mobileDoc, err := html.Parse(strings.NewReader(parts.MobileOutline.String())) + compareWithGolden(t, parts2, "deprecated", *update) + + bodyDoc, err := html.Parse(strings.NewReader(parts.Body.String())) if err != nil { t.Fatal(err) } + // Check that there are no duplicate id attributes. t.Run("duplicate ids", func(t *testing.T) { testDuplicateIDs(t, bodyDoc) @@ -71,31 +78,6 @@ func TestRenderParts(t *testing.T) { testIDsAndKinds(t, bodyDoc) }) - checker := in(".Documentation-note", - in("h3", hasAttr("id", "pkg-note-BUG"), hasExactText("Bugs ¶")), - in("a", hasHref("#pkg-note-BUG"))) - if err := checker(bodyDoc); err != nil { - t.Errorf("note check: %v", err) - } - - checker = in(".Documentation-index", - in(".Documentation-indexNote", in("a", hasHref("#pkg-note-BUG"), hasExactText("Bugs")))) - if err := checker(bodyDoc); err != nil { - t.Errorf("note check: %v", err) - } - - checker = in(".DocNav-notes", - in("#nav-group-notes", in("li", in("a", hasHref("#pkg-note-BUG"), hasText("Bugs"))))) - if err := checker(sidenavDoc); err != nil { - t.Errorf("note check: %v", err) - } - - checker = in("", - in("optgroup[label=Notes]", in("option", hasAttr("value", "pkg-note-BUG"), hasExactText("Bugs")))) - if err := checker(mobileDoc); err != nil { - t.Errorf("note check: %v", err) - } - wantLinks := []render.Link{ {Href: "https://go.googlesource.com/pkgsite", Text: "pkgsite repo"}, {Href: "https://play-with-go.dev", Text: "Play with Go"}, @@ -105,6 +87,11 @@ func TestRenderParts(t *testing.T) { } } +func compareWithGolden(t *testing.T, parts *Parts, name string, update bool) { + got := fmt.Sprintf("%s\n----\n%s\n----\n%s\n", parts.Body, parts.Outline, parts.MobileOutline) + testhelper.CompareWithGolden(t, got, name+".golden", update) +} + func TestExampleRender(t *testing.T) { LoadTemplates(templateSource) ctx := context.Background() @@ -353,33 +340,6 @@ func TestVersionedPkgPath(t *testing.T) { } } -func TestDeprecated(t *testing.T) { - LoadTemplates(templateSource) - ctx := context.Background() - fset, d := mustLoadPackage("deprecated") - parts, err := Render(ctx, fset, d, testRenderOptions) - if err != nil { - t.Fatal(err) - } - // In package deprecated, all non-deprecated symbols begin with "Good" and - // all deprecated ones begin with "Bad". - // There is one const, var, func and type of each. - - // The outline only has functions and types, so we should see two "Good"s and no "Bad"s. - outlineString := parts.Outline.String() - for _, want := range []string{"GoodF()", "type GoodT", "GoodM", "NewGoodTGood()"} { - if !strings.Contains(outlineString, want) { - t.Errorf("outline does not have %q but should", want) - } - } - for _, notWant := range []string{"BadF()", "type BadT", "BadM()", "NewGoodTBad()"} { - if strings.Contains(outlineString, notWant) { - t.Errorf("outline has %q but shouldn't", notWant) - } - } - -} - func testDuplicateIDs(t *testing.T, htmlDoc *html.Node) { idCounts := map[string]int{} walk(htmlDoc, func(n *html.Node) { diff --git a/internal/godoc/dochtml/testdata/deprecated.golden b/internal/godoc/dochtml/testdata/deprecated.golden new file mode 100644 index 000000000..7c2ff778b --- /dev/null +++ b/internal/godoc/dochtml/testdata/deprecated.golden @@ -0,0 +1,330 @@ + +
+

Overview

+ +
+
    +

    Package deprecated has some deprecated symbols. +

    +
    +

    Index

    + + +

    Constants

    + +
    +
    + View Source +
    const BadC = 2
    +
    +
    +
      +

      BadC is bad. +Deprecated: use GoodC. +

      + +
      + View Source +
      const GoodC = 1
      +
      + +
      + +

      Variables

      + +
      +
      + View Source +
      var BadV = 2 // Deprecated: use GoodV.
      +
      +
      + + +
      + View Source +
      var GoodV = 1
      +
      + +
      + +

      Functions

      + +

      + func BadF + + + +

      + +
      +
      func BadF()
      +
      +
      +
        +

        BadF is bad. +Deprecated: use GoodF. +

        +

        + func GoodF + + + +

        + +
        +
        func GoodF()
        +
        + +
        + +

        Types

        + +

        + type BadT + + + +

        + +
        +
        type BadT int
        +
        +
        +
          +

          BadT is bad. +Deprecated: use GoodT. +Don't use this. +

          +

          + type GoodT + + + +

          + +
          +
          type GoodT int
          +
          + +

          + func NewGoodTBad + + + +

          + +
          +
          func NewGoodTBad() GoodT
          +
          +
          +
            +

            NewGoodTBad is bad. +Deprecated: use NewGoodTGood. +

            +

            + func NewGoodTGood + + + +

            + +
            +
            func NewGoodTGood() GoodT
            +
            + +

            + func (GoodT) BadM + + + +

            + +
            +
            func (GoodT) BadM()
            +
            +
            +
              +

              BadM is bad. +Deprecated: use GoodM. +

              +

              + func (GoodT) GoodM + + + +

              + +
              +
              func (GoodT) GoodM()
              +
              + +
              + +---- + + + +---- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/internal/godoc/dochtml/testdata/everydecl.golden b/internal/godoc/dochtml/testdata/everydecl.golden new file mode 100644 index 000000000..c52d6c5a1 --- /dev/null +++ b/internal/godoc/dochtml/testdata/everydecl.golden @@ -0,0 +1,473 @@ + +
              +

              Overview

              + +
              +
                +

                Package everydecl has every form of declaration known to dochtml. +It is designed to test that the generated HTML has the right id and data-kind +attributes. +

                +
                +

                Index

                + + +

                Constants

                + +
                +
                + View Source +
                const C = 1
                +
                +
                +
                  +

                  const +

                  +
                  + +

                  Variables

                  + +
                  +
                  + View Source +
                  var V = 2
                  +
                  +
                  +
                    +

                    var +

                    +
                    + +

                    Functions

                    + +

                    + func F + + + +

                    + +
                    +
                    func F()
                    +
                    +
                    +
                      +

                      func +

                      +
                      + +

                      Types

                      + +

                      + type A + + + +

                      + +
                      +
                      type A int
                      +
                      + +

                      + type B + + + +

                      + +
                      +
                      type B bool
                      +
                      + +

                      + type I1 + + + +

                      + +
                      +
                      type I1 interface {
                      +	M1()
                      +}
                      +
                      + +

                      + type I2 + + + +

                      + +
                      +
                      type I2 interface {
                      +	I1 // embedded interface; should not have an id
                      +	M2()
                      +}
                      +
                      + +

                      + type S1 + + + +

                      + +
                      +
                      type S1 struct {
                      +	F int // field
                      +}
                      +
                      + +

                      + type S2 + + + +

                      + +
                      +
                      type S2 struct {
                      +	S1 // embedded struct; should have an id
                      +	G  int
                      +}
                      +
                      + +

                      + type T + + + +

                      + +
                      +
                      type T int
                      +
                      +
                      +
                        +

                        type +

                        +
                        +
                        +
                        const CT T = 3
                        +
                        +
                        +
                          +

                          typeConstant +

                          +
                          +
                          +
                          var VT T
                          +
                          +
                          +
                            +

                            typeVariable +

                            +

                            + func TF + + + +

                            + +
                            +
                            func TF() T
                            +
                            +
                            +
                              +

                              typeFunc +

                              +

                              + func (T) M + + + +

                              + +
                              +
                              func (T) M()
                              +
                              +
                              +
                                +

                                method +BUG(uid): this verifies that notes are rendered +

                                +

                                Notes

                                + +
                                +

                                Bugs

                                +
                                  +
                                • +
                                    +

                                    this verifies that notes are rendered +

                                  +
                                  + +---- + + + +---- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/internal/testing/testhelper/testhelper.go b/internal/testing/testhelper/testhelper.go index 89cec1c31..5492c1285 100644 --- a/internal/testing/testhelper/testhelper.go +++ b/internal/testing/testhelper/testhelper.go @@ -20,7 +20,9 @@ import ( "os" "path/filepath" "runtime" + "testing" + "github.com/google/go-cmp/cmp" "golang.org/x/pkgsite/internal/derrors" ) @@ -142,3 +144,31 @@ func CreateTestDirectory(files map[string]string) (_ string, err error) { return tempDir, nil } + +func CompareWithGolden(t *testing.T, got, filename string, update bool) { + t.Helper() + if update { + writeGolden(t, filename, got) + } else { + want := readGolden(t, filename) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("%s: mismatch (-want, +got):\n%s", filename, diff) + } + } +} + +func writeGolden(t *testing.T, name string, data string) { + filename := filepath.Join("testdata", name) + if err := ioutil.WriteFile(filename, []byte(data), 0644); err != nil { + t.Fatal(err) + } + t.Logf("wrote %s", filename) +} + +func readGolden(t *testing.T, name string) string { + data, err := ioutil.ReadFile(filepath.Join("testdata", name)) + if err != nil { + t.Fatal(err) + } + return string(data) +}