in internal/screentest/screentest.go [401:585]
func readTests(file string, vars map[string]string) ([]*testcase, error) {
tmpl, err := template.ParseFiles(file)
if err != nil {
return nil, fmt.Errorf("template.ParseFiles(%q): %w", file, err)
}
var tmplout bytes.Buffer
if err := tmpl.Execute(&tmplout, vars); err != nil {
return nil, fmt.Errorf("tmpl.Execute(...): %w", err)
}
var tests []*testcase
var (
testName, pathname string
tasks chromedp.Tasks
originA, originB string
headers map[string]interface{}
cacheA, cacheB bool
gcsBucket bool
width, height int
lineNo int
blockedURLs []string
)
cache, err := os.UserCacheDir()
if err != nil {
return nil, fmt.Errorf("os.UserCacheDir(): %w", err)
}
dir := filepath.Join(cache, "screentest")
out := outDir(dir, file)
scan := bufio.NewScanner(&tmplout)
for scan.Scan() {
lineNo += 1
line := strings.TrimSpace(scan.Text())
if strings.HasPrefix(line, "#") {
continue
}
line = strings.TrimRight(line, " \t")
field, args := splitOneField(line)
field = strings.ToUpper(field)
switch field {
case "":
// We've reached an empty line, reset properties scoped to a single test.
testName = ""
pathname = ""
tasks = nil
case "COMPARE":
origins := strings.Split(args, " ")
originA, originB = origins[0], origins[1]
cacheA, cacheB = false, false
if headers != nil {
headers = make(map[string]interface{})
}
if strings.HasSuffix(originA, cacheSuffix) {
originA = strings.TrimSuffix(originA, cacheSuffix)
cacheA = true
}
if strings.HasSuffix(originB, cacheSuffix) {
originB = strings.TrimSuffix(originB, cacheSuffix)
cacheB = true
}
if _, err := url.Parse(originA); err != nil {
return nil, fmt.Errorf("url.Parse(%q): %w", originA, err)
}
if _, err := url.Parse(originB); err != nil {
return nil, fmt.Errorf("url.Parse(%q): %w", originB, err)
}
case "HEADER":
if headers == nil {
headers = make(map[string]interface{})
}
parts := strings.SplitN(args, ":", 2)
if len(parts) != 2 {
log.Fatalf("invalid header %s on line %d", args, lineNo)
}
headers[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
case "OUTPUT":
if strings.HasPrefix(args, gcsScheme) {
gcsBucket = true
}
out = outDir(args, "")
case "WINDOWSIZE":
width, height, err = splitDimensions(args)
if err != nil {
return nil, fmt.Errorf("splitDimensions(%q): %w", args, err)
}
case "TEST":
testName = args
for _, t := range tests {
if t.name == testName {
return nil, fmt.Errorf(
"duplicate test name %q on line %d", testName, lineNo)
}
}
case "PATHNAME":
if _, err := url.Parse(originA + args); err != nil {
return nil, fmt.Errorf("url.Parse(%q): %w", originA+args, err)
}
if _, err := url.Parse(originB + args); err != nil {
return nil, fmt.Errorf("url.Parse(%q): %w", originB+args, err)
}
pathname = args
if testName == "" {
testName = pathname[1:]
}
for _, t := range tests {
if t.name == testName {
return nil, fmt.Errorf(
"duplicate test with pathname %q on line %d", pathname, lineNo)
}
}
case "CLICK":
tasks = append(tasks, chromedp.Click(args))
case "WAIT":
tasks = append(tasks, chromedp.WaitReady(args))
case "EVAL":
tasks = append(tasks, chromedp.Evaluate(args, nil))
case "BLOCK":
blockedURLs = append(blockedURLs, strings.Fields(args)...)
case "CAPTURE":
if originA == "" || originB == "" {
return nil, fmt.Errorf("missing compare for capture on line %d", lineNo)
}
if pathname == "" {
return nil, fmt.Errorf("missing pathname for capture on line %d", lineNo)
}
urlA, err := url.Parse(originA + pathname)
if err != nil {
return nil, fmt.Errorf("url.Parse(%q): %w", originA+pathname, err)
}
urlB, err := url.Parse(originB + pathname)
if err != nil {
return nil, fmt.Errorf("url.Parse(%q): %w", originB+pathname, err)
}
test := &testcase{
name: testName,
tasks: tasks,
urlA: urlA.String(),
urlB: urlB.String(),
headers: headers,
blockedURLs: blockedURLs,
// Default to viewportScreenshot
screenshotType: viewportScreenshot,
viewportWidth: width,
viewportHeight: height,
cacheA: cacheA,
cacheB: cacheB,
gcsBucket: gcsBucket,
}
tests = append(tests, test)
field, args := splitOneField(args)
field = strings.ToUpper(field)
switch field {
case "FULLSCREEN", "VIEWPORT":
if field == "FULLSCREEN" {
test.screenshotType = fullScreenshot
}
if args != "" {
w, h, err := splitDimensions(args)
if err != nil {
return nil, fmt.Errorf("splitDimensions(%q): %w", args, err)
}
test.name += fmt.Sprintf(" %dx%d", w, h)
test.viewportWidth = w
test.viewportHeight = h
}
case "ELEMENT":
test.name += fmt.Sprintf(" %s", args)
test.screenshotType = elementScreenshot
test.screenshotElement = args
}
outfile := filepath.Join(out, sanitized(test.name))
if gcsBucket {
outfile = out + "/" + sanitized(test.name)
}
test.outImgA = outfile + ".a.png"
test.outImgB = outfile + ".b.png"
test.outDiff = outfile + ".diff.png"
default:
// We should never reach this error.
return nil, fmt.Errorf("invalid syntax on line %d: %q", lineNo, line)
}
}
if err := scan.Err(); err != nil {
return nil, fmt.Errorf("scan.Err(): %v", err)
}
return tests, nil
}