Skip to content

Commit

Permalink
internal/screentest: combine main and screentest
Browse files Browse the repository at this point in the history
This CL follows up on the previous one by merging the code of
the main program with the code of the former internal/screentest
package. Changes include a single struct for options/flags,
and putting all user documentation into one place.

There are no behavior changes.

Change-Id: Icb87309769bf9983382402f364a2e290af0e5076
Reviewed-on: https://go-review.googlesource.com/c/website/+/627058
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Hyang-Ah Hana Kim <[email protected]>
  • Loading branch information
jba committed Nov 14, 2024
1 parent 0969b1b commit ffc202c
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 247 deletions.
202 changes: 141 additions & 61 deletions cmd/screentest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,166 @@
// license that can be found in the LICENSE file.

/*
Command screentest runs the screentest check for a set of scripts.
Screentest compares images of rendered web pages.
It compares images obtained from two sources, one to test and one for the expected result.
The comparisons are driven by a script file in a format described below.
Usage: screentest [flags] [glob]
# Usage
screentest [flags] [glob]
The flags are:
-test
URL to test against
-want
URL for expected results
-test URL
URL or path being tested. Required.
-want URL
URL or path for expected results. Required.
-c
Number of test cases to run concurrently.
-d
URL of a Chrome websocket debugger. If omitted, screentest tries to find the
Chrome executable on the system and starts a new instance.
-headers
HTTP headers to send
HTTP(S) headers to send with each request, as a comma-separated list of name:value.
-run REGEXP
Run only tests matching regexp.
-o
URL for output
URL or path for output files. If omitted, files are written to a subdirectory of the
user's cache directory.
-u
update cached screenshots
Update cached screenshots.
-v
variables provided to script templates as comma separated KEY:VALUE pairs
-c
number of testcases to run concurrently
-d
chrome debugger url
-run
run only tests matching regexp
Variables provided to script templates as comma separated KEY:VALUE pairs.
# Scripts
A script file contains one or more test cases described as a sequence of lines. The
file is first processed as Go template using the text/template package, with a map
of the variables given by the -v flag provided as `.`.
The script format is line-oriented.
Lines beginning with # characters are ignored as comments.
Each non-blank, non-comment line is a directive, listed below.
Each test case begins with the 'test' directive and ends with a blank line.
A test case describes actions to take on a page, along
with the dimensions of the screenshots to be compared. For example, here is
a trivial script:
test about
pathname /about
capture fullscreen
This script has a single test case. The first line names the test.
The second line sets the page to visit at each origin. The last line
captures full-page screenshots of the pages and generates a diff image if they
do not match.
# Directives
Use windowsize WIDTHxHEIGHT to set the default window size for all test cases
that follow.
windowsize 540x1080
Use block URL ... to set URL patterns to block. Wildcards ('*') are allowed.
block https://codecov.io/* https://travis-ci.com/*
The directives above apply to all test cases that follow.
The ones below must appear inside a test case and apply only to that case.
Use test NAME to create a name for the test case.
test about page
Use pathname PATH to set the page to visit at each origin.
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
Use wait SELECTOR to wait for an element to appear.
wait [role="treeitem"][aria-expanded="true"]
Use capture [SIZE] [ARG] to create a test case with the properties
defined in the test case. If present, the first argument to capture should be one of
'fullscreen', 'viewport' or 'element'.
capture fullscreen 540x1080
When taking an element screenshot provide a selector.
capture element header
Use eval JS to evaluate JavaScript snippets to hide elements or prepare the page in
some other way.
eval 'document.querySelector(".selector").remove();'
eval 'window.scrollTo({top: 0});'
Each capture command to creates a new test case for a single page.
windowsize 1536x960
test homepage
pathname /
capture viewport
capture viewport 540x1080
capture viewport 400x1000
test about page
pathname /about
capture viewport
capture viewport 540x1080
capture viewport 400x1000
*/
package main

import (
"context"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
)

var (
testURL = flag.String("test", "", "URL or file path to test")
wantURL = flag.String("want", "", "URL or file path with expected results")
update = flag.Bool("u", false, "update cached screenshots")
vars = flag.String("v", "", "variables provided to script templates as comma separated KEY:VALUE pairs")
concurrency = flag.Int("c", (runtime.NumCPU()+1)/2, "number of testcases to run concurrently")
debuggerURL = flag.String("d", "", "chrome debugger url")
run = flag.String("run", "", "regexp to match test")
outputURL = flag.String("o", "", "path for output: file path or URL with 'file' or 'gs' scheme")
headers = flag.String("headers", "", "HTTP headers: comma-separated list of name:value")
)
var flags options

func init() {
flag.StringVar(&flags.testURL, "test", "", "URL or file path to test")
flag.StringVar(&flags.wantURL, "want", "", "URL or file path with expected results")
flag.BoolVar(&flags.update, "u", false, "update cached screenshots")
flag.StringVar(&flags.vars, "v", "", "variables provided to script templates as comma separated KEY:VALUE pairs")
flag.IntVar(&flags.maxConcurrency, "c", (runtime.NumCPU()+1)/2, "number of test cases to run concurrently")
flag.StringVar(&flags.debuggerURL, "d", "", "chrome debugger URL")
flag.StringVar(&flags.outputURL, "o", "", "path for output: file path or URL with 'file' or 'gs' scheme")
flag.StringVar(&flags.headers, "headers", "", "HTTP headers: comma-separated list of name:value")
flag.StringVar(&flags.run, "run", "", "regexp to match test")
}

// options are the options for the program.
// See the top command and the flag.XXXVar calls above for documentation.
type options struct {
testURL string
wantURL string
update bool
vars string
maxConcurrency int
debuggerURL string
run string
outputURL string
headers string
}

func main() {
flag.Usage = func() {
Expand All @@ -69,39 +180,8 @@ func main() {
if len(args) == 1 {
glob = args[0]
}
parsedVars := make(map[string]string)
if *vars != "" {
for _, pair := range strings.Split(*vars, ",") {
parts := strings.SplitN(pair, ":", 2)
if len(parts) != 2 {
log.Fatal(fmt.Errorf("invalid key value pair, %q", pair))
}
parsedVars[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
}

var splitHeaders []string
if len(*headers) > 0 {
splitHeaders = strings.Split(*headers, ",")
}
opts := CheckOptions{
TestURL: *testURL,
WantURL: *wantURL,
Update: *update,
MaxConcurrency: *concurrency,
Vars: parsedVars,
DebuggerURL: *debuggerURL,
OutputURL: *outputURL,
Headers: splitHeaders,
}
if *run != "" {
re, err := regexp.Compile(*run)
if err != nil {
log.Fatal(err)
}
opts.Filter = re.MatchString
}
if err := CheckHandler(glob, opts); err != nil {
if err := run(context.Background(), glob, flags); err != nil {
log.Fatal(err)
}
}
Loading

0 comments on commit ffc202c

Please sign in to comment.