diff --git a/internal/frontend/server_test.go b/internal/frontend/server_test.go index 15e86df5..91033951 100644 --- a/internal/frontend/server_test.go +++ b/internal/frontend/server_test.go @@ -17,7 +17,7 @@ import ( "golang.org/x/discovery/internal" "golang.org/x/discovery/internal/middleware" "golang.org/x/discovery/internal/postgres" - "golang.org/x/discovery/internal/stdlib" + "golang.org/x/discovery/internal/source" "golang.org/x/discovery/internal/testing/htmlcheck" "golang.org/x/discovery/internal/testing/pagecheck" "golang.org/x/discovery/internal/testing/sample" @@ -47,6 +47,18 @@ func TestHTMLInjection(t *testing.T) { } } +// TestServer checks the contents of served pages by looking for +// strings and elements in the parsed HTML response body. +// +// Other than search and static content, our pages vary along five dimensions: +// +// 1. module / package / directory +// 2. stdlib / other (since the standard library is a special case in several ways) +// 3. redistributable / non-redistributable +// 4. versioned / unversioned URL (whether the URL for the page contains "@version") +// 5. the tab (overview / doc / imports / ...) +// +// We aim to test all combinations of these. func TestServer(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() @@ -58,6 +70,7 @@ func TestServer(t *testing.T) { v.ModulePath = modulePath v.Version = version v.Packages = pkgs + v.SourceInfo = source.NewGitHubInfo(sample.RepositoryURL, "", version) if err := testDB.InsertVersion(ctx, v); err != nil { t.Fatal(err) } @@ -88,7 +101,7 @@ func TestServer(t *testing.T) { pkgCmdGo := sample.Package() pkgCmdGo.Name = "main" pkgCmdGo.Path = "cmd/go" - mustInsertVersion(stdlib.ModulePath, "v1.13.0", []*internal.Package{pkgCmdGo}) + mustInsertVersion("std", "v1.13.0", []*internal.Package{pkgCmdGo}) s, err := NewServer(testDB, nil, "../../content/static", false) if err != nil { @@ -338,11 +351,14 @@ func TestServer(t *testing.T) { wantStatusCode: http.StatusOK, want: in("", pagecheck.PackageHeader(pkgV100, versioned), - in(".Overview-sourceCodeLink a", - href("github.com/valid_module_name"), - text("github.com/valid_module_name")), - in(".Overview-readmeContent", text("readme")), - in(".Overview-readmeSource", text("Source: github.com/valid_module_name@v1.0.0/README.md"))), + pagecheck.OverviewDetails(&pagecheck.Overview{ + ModuleLink: "/mod/github.com/valid_module_name@v1.0.0", + ModuleLinkText: pkgV100.ModulePath, + RepoURL: "https://github.com/valid_module_name", + PackageURL: "https://github.com/valid_module_name/tree/v1.0.0/foo", + ReadmeContent: "readme", + ReadmeSource: "github.com/valid_module_name@v1.0.0/README.md", + })), }, { name: "package@version readme tab nonredistributable", @@ -436,19 +452,13 @@ func TestServer(t *testing.T) { wantStatusCode: http.StatusOK, want: in("", pagecheck.DirectoryHeader(dir, unversioned), - in(".Overview-module", - text("Module"), - in("a", - href("/mod/github.com/valid_module_name"), - text("github.com/valid_module_name"))), - in(".Overview-sourceCodeLink", - text("Repository"), - in("a", - href("github.com/valid_module_name"), - attr("target", "_blank"), - text("github.com/valid_module_name"))), - in(".Overview-readmeContent", text("readme")), - in(".Overview-readmeSource", text("Source: github.com/valid_module_name@v1.0.0/README.md"))), + pagecheck.OverviewDetails(&pagecheck.Overview{ + ModuleLink: "/mod/github.com/valid_module_name", + ModuleLinkText: dir.ModulePath, + RepoURL: "https://github.com/valid_module_name", + ReadmeContent: "readme", + ReadmeSource: "github.com/valid_module_name@v1.0.0/README.md", + })), }, { name: "directory licenses", @@ -481,21 +491,13 @@ func TestServer(t *testing.T) { wantStatusCode: http.StatusOK, want: in("", pagecheck.DirectoryHeader(dirCmd, versioned), - - in(".Overview-module", - text("Standard Library"), - in("a", - href("/std@go1.13"), - text("Standard Library"))), - in(".Overview-sourceCodeLink", - text("Repository"), - in("a", - href("github.com/valid_module_name"), - attr("target", "_blank"), - text("github.com/valid_module_name"))), - in(".Overview-readmeContent", text("readme")), - in(".Overview-readmeSource", - text(`^Source: go.googlesource.com/go/\+/refs/tags/go1.13/README.md$`))), + pagecheck.OverviewDetails(&pagecheck.Overview{ + ModuleLink: "/std@go1.13", + ModuleLinkText: "Standard Library", + ReadmeContent: "readme", + RepoURL: "https://github.com/valid_module_name", // wrong, but hard to change + ReadmeSource: "go.googlesource.com/go/+/refs/tags/go1.13/README.md", + })), }, { name: "stdlib directory licenses", @@ -513,7 +515,13 @@ func TestServer(t *testing.T) { // Fall back to the latest version, show readme tab by default. want: in("", pagecheck.ModuleHeader(mod, unversioned), - in(".Overview-readmeContent", text(`readme`))), + pagecheck.OverviewDetails(&pagecheck.Overview{ + ModuleLink: "/mod/" + sample.ModulePath, + ModuleLinkText: sample.ModulePath, + ReadmeContent: "readme", + RepoURL: "https://github.com/valid_module_name", + ReadmeSource: "github.com/valid_module_name@v1.0.0/README.md", + })), }, { name: "module overview", @@ -523,10 +531,13 @@ func TestServer(t *testing.T) { // Fall back to the latest version, show readme tab by default. want: in("", pagecheck.ModuleHeader(mod, unversioned), - in(".Overview-module a", - href("/mod/"+sample.ModulePath), - text("^"+sample.ModulePath+"$")), - in(".Overview-readmeContent", text(`readme`))), + pagecheck.OverviewDetails(&pagecheck.Overview{ + ModuleLink: "/mod/" + sample.ModulePath, + ModuleLinkText: sample.ModulePath, + ReadmeContent: "readme", + RepoURL: "https://github.com/valid_module_name", + ReadmeSource: "github.com/valid_module_name@v1.0.0/README.md", + })), }, { name: "module overview pseudoversion latest", @@ -558,10 +569,13 @@ func TestServer(t *testing.T) { wantStatusCode: http.StatusOK, want: in("", pagecheck.ModuleHeader(mod, versioned), - in(".Overview-module a", - href(fmt.Sprintf("/mod/%s@%s", sample.ModulePath, sample.VersionString)), - text("^"+sample.ModulePath+"$")), - in(".Overview-readmeContent", text(`readme`))), + pagecheck.OverviewDetails(&pagecheck.Overview{ + ModuleLink: fmt.Sprintf("/mod/%s@%s", sample.ModulePath, sample.VersionString), + ModuleLinkText: sample.ModulePath, + ReadmeContent: "readme", + RepoURL: "https://github.com/valid_module_name", + ReadmeSource: "github.com/valid_module_name@v1.0.0/README.md", + })), }, { name: "module@version overview tab, pseudoversion", @@ -569,10 +583,13 @@ func TestServer(t *testing.T) { wantStatusCode: http.StatusOK, want: in("", pagecheck.ModuleHeader(modPseudo, versioned), - in(".Overview-module a", - href(fmt.Sprintf("/mod/%s@%s", sample.ModulePath, pseudoVersion)), - text("^"+sample.ModulePath+"$")), - in(".Overview-readmeContent", text(`readme`))), + pagecheck.OverviewDetails(&pagecheck.Overview{ + ModuleLink: fmt.Sprintf("/mod/%s@%s", sample.ModulePath, pseudoVersion), + ModuleLinkText: sample.ModulePath, + ReadmeContent: "readme", + RepoURL: "https://github.com/valid_module_name", + ReadmeSource: "github.com/valid_module_name@" + pseudoVersion + "/README.md", + })), }, { name: "module@version packages tab", diff --git a/internal/testing/htmlcheck/htmlcheck.go b/internal/testing/htmlcheck/htmlcheck.go index a75068fc..a9697667 100644 --- a/internal/testing/htmlcheck/htmlcheck.go +++ b/internal/testing/htmlcheck/htmlcheck.go @@ -24,6 +24,8 @@ type Checker func(*html.Node) error // // Calling In(selector), with no checkers, just checks for the presence of // a node matching the selector. (For the negation, see NotIn.) +// +// A nil Checker is valid and always succeeds. func In(selector string, checkers ...Checker) Checker { sel := mustParseSelector(selector) return func(n *html.Node) error { @@ -103,6 +105,9 @@ func NotIn(selector string) Checker { // check calls all the Checkers on n, returning the error of the first one to fail. func check(n *html.Node, Checkers []Checker) error { for _, m := range Checkers { + if m == nil { + continue + } if err := m(n); err != nil { return err } diff --git a/internal/testing/pagecheck/pagecheck.go b/internal/testing/pagecheck/pagecheck.go index 017f4ec0..81ed4b1e 100644 --- a/internal/testing/pagecheck/pagecheck.go +++ b/internal/testing/pagecheck/pagecheck.go @@ -30,6 +30,16 @@ type Page struct { ModuleURL string // the relative module URL } +// Overview describes the contents of the overview tab. +type Overview struct { + ModuleLink string // relative link to module page + ModuleLinkText string + RepoURL string + PackageURL string + ReadmeContent string + ReadmeSource string +} + var ( in = htmlcheck.In inAt = htmlcheck.InAt @@ -108,6 +118,26 @@ func LicenseDetails(ltype, bodySubstring, source string) htmlcheck.Checker { exactText("Source: "+source))) } +// OverviewDetails checks the details section of an overview tab. +func OverviewDetails(ov *Overview) htmlcheck.Checker { + var pkg htmlcheck.Checker + if ov.PackageURL != "" { + pkg = inAt(".Overview-sourceCodeLink a", 1, + href(ov.PackageURL), + exactText(ov.PackageURL)) + } + return in("", + in("div.Overview-module > a", + href(ov.ModuleLink), + exactText(ov.ModuleLinkText)), + inAt(".Overview-sourceCodeLink a", 0, + href(ov.RepoURL), + exactText(ov.RepoURL)), + pkg, + in(".Overview-readmeContent", text(ov.ReadmeContent)), + in(".Overview-readmeSource", exactText("Source: "+ov.ReadmeSource))) +} + // versionBadge checks the latest-version badge on a header. func versionBadge(p *Page) htmlcheck.Checker { class := "DetailsHeader-" diff --git a/internal/testing/sample/sample.go b/internal/testing/sample/sample.go index 9302f394..e8adc973 100644 --- a/internal/testing/sample/sample.go +++ b/internal/testing/sample/sample.go @@ -22,7 +22,7 @@ import ( // These sample values can be used to construct test cases. var ( ModulePath = "github.com/valid_module_name" - RepositoryURL = "github.com/valid_module_name" + RepositoryURL = "https://github.com/valid_module_name" VersionString = "v1.0.0" CommitTime = NowTruncated() LicenseMetadata = []*license.Metadata{