internal/frontend: add client.Search

A client.Search method is added, which will be used for frontend tests
in a later CL.

For golang/go#44142

Change-Id: I5aa7e6606777d943accf956c8e77a94dcb3746cd
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/346414
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Julie Qiu <julie@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
diff --git a/internal/frontend/client.go b/internal/frontend/client.go
index 3368eb2..2012877 100644
--- a/internal/frontend/client.go
+++ b/internal/frontend/client.go
@@ -9,6 +9,7 @@
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"net/url"
 	"os"
 
 	"golang.org/x/pkgsite/internal/auth"
@@ -53,6 +54,21 @@
 	return &vd, nil
 }
 
+// Search returns a SearchPage for a search query and mode.
+func (c *Client) Search(q, mode string) (_ *SearchPage, err error) {
+	defer derrors.Wrap(&err, "Search(%q)", q)
+	u := fmt.Sprintf("%s/search?q=%s&content=json&m=%s", c.url, url.QueryEscape(q), mode)
+	body, err := c.fetchJSONPage(u)
+	if err != nil {
+		return nil, err
+	}
+	var sp SearchPage
+	if err := json.Unmarshal(body, &sp); err != nil {
+		return nil, fmt.Errorf("json.Unmarshal: %v:\nDoes GO_DISCOVERY_SERVE_STATS=true on the frontend?", err)
+	}
+	return &sp, nil
+}
+
 func (c *Client) fetchJSONPage(url string) (_ []byte, err error) {
 	defer derrors.Wrap(&err, "fetchJSONPage(%q)", url)
 	r, err := c.httpClient.Get(url)
diff --git a/internal/frontend/search.go b/internal/frontend/search.go
index ca25d95..24e538f 100644
--- a/internal/frontend/search.go
+++ b/internal/frontend/search.go
@@ -324,7 +324,9 @@
 	if searchSymbols {
 		page.SearchMode = searchModeSymbol
 	}
-
+	if s.shouldServeJSON(r) {
+		return s.serveJSONPage(w, r, page)
+	}
 	tmpl := "legacy_search"
 	if experiment.IsActive(ctx, internal.ExperimentSearchGrouping) {
 		tmpl = "search"