internal/screentest: check http status on page load
Requests to URLs are expected to return HTTP Status 200.
To override the expected status code, testcases can use
the status keyword.
Change-Id: Ibe6696fc2fe2a779755ff96dc8c5532c1ce03c66
Reviewed-on: https://go-review.googlesource.com/c/website/+/384840
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Trust: Jamal Carvalho <jamalcarvalho@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/screentest/screentest.go b/internal/screentest/screentest.go
index 65660f4..e7f4aa7 100644
--- a/internal/screentest/screentest.go
+++ b/internal/screentest/screentest.go
@@ -55,6 +55,9 @@
//
// output gs://bucket-name
//
+// Values set with the keywords above apply to all testcases that follow. Values set with
+// the keywords below reset each time the test keyword is used.
+//
// Use test NAME to create a name for the testcase.
//
// test about page
@@ -64,6 +67,10 @@
//
// pathname /about
//
+// Use status CODE to set an expected HTTP status code. The default is 200.
+//
+// status 404
+//
// Use click SELECTOR to add a click an element on the page.
//
// click button.submit
@@ -113,6 +120,7 @@
"io/fs"
"io/ioutil"
"log"
+ "net/http"
"net/url"
"os"
"path/filepath"
@@ -382,6 +390,7 @@
tasks chromedp.Tasks
urlA, urlB string
headers map[string]interface{}
+ status int
cacheA, cacheB bool
gcsBucket bool
outImgA, outImgB, outDiff string
@@ -413,6 +422,7 @@
tasks chromedp.Tasks
originA, originB string
headers map[string]interface{}
+ status int = http.StatusOK
cacheA, cacheB bool
gcsBucket bool
width, height int
@@ -438,9 +448,9 @@
switch field {
case "":
// We've reached an empty line, reset properties scoped to a single test.
- testName = ""
- pathname = ""
+ testName, pathname = "", ""
tasks = nil
+ status = http.StatusOK
case "COMPARE":
origins := strings.Split(args, " ")
originA, originB = origins[0], origins[1]
@@ -471,6 +481,11 @@
log.Fatalf("invalid header %s on line %d", args, lineNo)
}
headers[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
+ case "STATUS":
+ status, err = strconv.Atoi(args)
+ if err != nil {
+ return nil, fmt.Errorf("strconv.Atoi(%q): %w", args, err)
+ }
case "OUTPUT":
if strings.HasPrefix(args, gcsScheme) {
gcsBucket = true
@@ -535,6 +550,7 @@
urlA: urlA.String(),
urlB: urlB.String(),
headers: headers,
+ status: status,
blockedURLs: blockedURLs,
// Default to viewportScreenshot
screenshotType: viewportScreenshot,
@@ -736,6 +752,10 @@
return &img, nil
}
+type Response struct {
+ Status int
+}
+
// captureScreenshot runs a series of browser actions and takes a screenshot
// of the resulting webpage in an instance of headless chrome.
func (tc *testcase) captureScreenshot(ctx context.Context, url string) ([]byte, error) {
@@ -751,7 +771,9 @@
if tc.blockedURLs != nil {
tasks = append(tasks, network.SetBlockedURLS(tc.blockedURLs))
}
+ var res Response
tasks = append(tasks,
+ getResponse(url, &res),
chromedp.EmulateViewport(int64(tc.viewportWidth), int64(tc.viewportHeight)),
chromedp.Navigate(url),
waitForEvent("networkIdle"),
@@ -769,6 +791,9 @@
if err := chromedp.Run(ctx, tasks); err != nil {
return nil, fmt.Errorf("chromedp.Run(...): %w", err)
}
+ if res.Status != tc.status {
+ return nil, fmt.Errorf("http status mismatch: got %d; want %d", res.Status, tc.status)
+ }
return buf, nil
}
@@ -864,6 +889,20 @@
}
}
+func getResponse(url string, res *Response) chromedp.ActionFunc {
+ return func(ctx context.Context) error {
+ chromedp.ListenTarget(ctx, func(ev interface{}) {
+ switch e := ev.(type) {
+ case *network.EventResponseReceived:
+ if e.Response.URL == url {
+ res.Status = int(e.Response.Status)
+ }
+ }
+ })
+ return nil
+ }
+}
+
// runConcurrently calls f on each integer from 0 to n-1,
// with at most max invocations active at once.
// It waits for all invocations to complete.
diff --git a/internal/screentest/screentest_test.go b/internal/screentest/screentest_test.go
index 14f7b9a..9c0c8fd 100644
--- a/internal/screentest/screentest_test.go
+++ b/internal/screentest/screentest_test.go
@@ -43,6 +43,7 @@
name: "go.dev homepage",
urlA: "https://go.dev/",
urlB: "http://localhost:6060/go.dev/",
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "go-dev-homepage.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "go-dev-homepage.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "go-dev-homepage.diff.png"),
@@ -54,6 +55,7 @@
name: "go.dev homepage 540x1080",
urlA: "https://go.dev/",
urlB: "http://localhost:6060/go.dev/",
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "go-dev-homepage-540x1080.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "go-dev-homepage-540x1080.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "go-dev-homepage-540x1080.diff.png"),
@@ -65,6 +67,7 @@
name: "about page",
urlA: "https://go.dev/about",
urlB: "http://localhost:6060/go.dev/about",
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "about-page.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "about-page.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "about-page.diff.png"),
@@ -76,6 +79,7 @@
name: "pkg.go.dev homepage .go-Carousel",
urlA: "https://pkg.go.dev/",
urlB: "https://beta.pkg.go.dev/",
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "pkg-go-dev-homepage--go-Carousel.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "pkg-go-dev-homepage--go-Carousel.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "pkg-go-dev-homepage--go-Carousel.diff.png"),
@@ -91,6 +95,7 @@
name: "net package doc",
urlA: "https://pkg.go.dev/net",
urlB: "https://beta.pkg.go.dev/net",
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "net-package-doc.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "net-package-doc.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "net-package-doc.diff.png"),
@@ -105,6 +110,7 @@
name: "net package doc 540x1080",
urlA: "https://pkg.go.dev/net",
urlB: "https://beta.pkg.go.dev/net",
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "net-package-doc-540x1080.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "net-package-doc-540x1080.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "net-package-doc-540x1080.diff.png"),
@@ -121,6 +127,7 @@
cacheA: true,
urlB: "http://localhost:8080/about",
headers: map[string]interface{}{"Authorization": "Bearer token"},
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "about.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "about.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "about.diff.png"),
@@ -134,6 +141,7 @@
cacheA: true,
urlB: "http://localhost:8080/eval",
headers: map[string]interface{}{"Authorization": "Bearer token"},
+ status: 200,
outImgA: filepath.Join(cache, "readtests-txt", "eval.a.png"),
outImgB: filepath.Join(cache, "readtests-txt", "eval.b.png"),
outDiff: filepath.Join(cache, "readtests-txt", "eval.diff.png"),
@@ -151,6 +159,7 @@
urlB: "http://localhost:8080/gcs-output",
gcsBucket: true,
headers: map[string]interface{}{"Authorization": "Bearer token"},
+ status: 200,
outImgA: "gs://bucket-name/gcs-output.a.png",
outImgB: "gs://bucket-name/gcs-output.b.png",
outDiff: "gs://bucket-name/gcs-output.diff.png",
diff --git a/internal/screentest/testdata/pass.txt b/internal/screentest/testdata/pass.txt
index 9e933de..4087561 100644
--- a/internal/screentest/testdata/pass.txt
+++ b/internal/screentest/testdata/pass.txt
@@ -3,3 +3,7 @@
test homepage
pathname /
capture viewport
+
+pathname /page-not-found
+status 404
+capture