Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
Package htmlcheck provides a set of functions that check for properties of a parsed HTML document.
package htmlcheck

import (
	
	
	
	

	
	
)
A Checker is a function from an HTML node to an error describing a failure.
type Checker func(*html.Node) error
Run is a convenience function to run the checker against HTML read from reader.
func ( io.Reader,  Checker) error {
	,  := html.Parse()
	if  != nil {
		return 
	}
	return ()
}
In returns a Checker that applies the given checkers to the first node matching the CSS selector. The empty selector denotes the entire subtree of the Checker's argument node. 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 ( string,  ...Checker) Checker {
	 := mustParseSelector()
	return func( *html.Node) error {
cascadia.Query does not test against its argument node.
		if .Match() {
			 = 
		} else {
			 = cascadia.Query(, )
		}
		if  == nil {
			return fmt.Errorf("no element matches selector %q", )
		}
		if  := check(, );  != nil {
			if  == "" {
				return 
			}
			return fmt.Errorf("%s: %v", , )
		}
		return nil
	}
}
InAll runs the checkers against all nodes matching selector.
func ( string,  ...Checker) Checker {
	 := mustParseSelector()
	return func( *html.Node) error {
		 := allMatching(, )
		for ,  := range  {
			if  := check(, );  != nil {
				return fmt.Errorf("%s, #%d: %v", , , )
			}
		}
		return nil
	}
}

func ( *html.Node,  cascadia.Sel) []*html.Node {
	var  []*html.Node
	if .Match() {
		 = append(, )
	}
	return append(, cascadia.QueryAll(, )...)
}
NotIn returns a checker that succeeds only if no nodes match selector.
func ( string) Checker {
	 := mustParseSelector()
	return func( *html.Node) error {
		if .Match() || cascadia.Query(, ) != nil {
			return fmt.Errorf("%q matched one or more elements", )
		}
		return nil
	}
}
check calls all the Checkers on n, returning the error of the first one to fail.
func ( *html.Node,  []Checker) error {
	for ,  := range  {
		if  == nil {
			continue
		}
		if  := ();  != nil {
			return 
		}
	}
	return nil
}
mustParseSelector parses the given CSS selector. An empty string is treated as "*" (match everything).
func ( string) cascadia.Sel {
	if  == "" {
		 = "*"
	}
	,  := cascadia.Parse()
	if  != nil {
		panic(fmt.Sprintf("parsing %q: %v", , ))
	}
	return 
}
HasText returns a Checker that checks whether the given regexp matches the node's text. The text of a node n is the concatenated contents of all text nodes in n's subtree. HasText panics if the argument doesn't compile.
func ( string) Checker {
	 := regexp.MustCompile()
	return func( *html.Node) error {
		var  strings.Builder
		nodeText(, &)
		 := .String()
		if !.MatchString() {
			if len() > 100 {
				 = [:97] + "..."
			}
			return fmt.Errorf("\n`%s` does not match\n%q", , )
		}
		return nil
	}
}
HasExactText returns a checker that checks whether the given string matches the node's text exactly.
func ( string) Checker {
	return HasText("^" + regexp.QuoteMeta() + "$")
}
HasExactTextCollapsed returns a checker that checks whether the given string matches the node's text with its leading, trailing, and redundant whitespace trimmed.
func ( string) Checker {
	 := strings.Join(strings.Fields(strings.TrimSpace(regexp.QuoteMeta())), `\s*`)
	return HasText(`^\s*` +  + `\s*$`)
}
nodeText appends the text of n's subtree to b. This is the concatenated contents of all text nodes, visited depth-first.
func ( *html.Node,  *strings.Builder) {
	if  == nil {
		return
	}
	switch .Type {
	case html.TextNode:
		.WriteString(.Data)
	case html.ElementNode, html.DocumentNode:
		for  := .FirstChild;  != nil;  = .NextSibling {
			(, )
		}
	}
}
HasAttr returns a Checker that checks for an attribute with the given name whose value matches the given regular expression. HasAttr panics if wantValRegexp does not compile.
func (,  string) Checker {
	 := regexp.MustCompile()
	return func( *html.Node) error {
		for ,  := range .Attr {
			if .Key ==  {
				if !.MatchString(.Val) {
					return fmt.Errorf("[%q]:\n`%s` does not match\n%q", , , .Val)
				}
				return nil
			}
		}
		return fmt.Errorf("[%q]: no such attribute", )
	}
}
HasHref returns a Checker that checks whether the node has an "href" attribute with exactly val.
func ( string) Checker {
	return HasAttr("href", "^"+regexp.QuoteMeta()+"$")
}
Dump returns a Checker that always returns nil, and as a side-effect writes a human-readable description of n's subtree to standard output. It is useful for debugging.
func () Checker {
	return func( *html.Node) error {
		dump(, 0)
		return nil
	}
}

func ( *html.Node,  int) {
	for  := 0;  < ; ++ {
		fmt.Print("  ")
	}
	fmt.Printf("type %d, data %q, attr %v\n", .Type, .Data, .Attr)
	for  := .FirstChild;  != nil;  = .NextSibling {
		(, +1)
	}