package cascadia import ( "bytes" "strings" "testing" "golang.org/x/net/html" ) type selectorTest struct { HTML, selector string results []string } func nodeString(n *html.Node) string { buf := bytes.NewBufferString("") html.Render(buf, n) return buf.String() } var selectorTests = []selectorTest{ { `
This address...`, "address", []string{ "This address...", }, }, { `text`, "*", []string{ "text", "", "text", }, }, { ``, "*", []string{ "", "", "", }, }, { ``, "#foo", []string{ `
`, }, }, { ``, "li#t1", []string{ `
`, "p.t1", []string{ `
`, }, }, { ``, ".t1.fail", []string{}, }, { `
`, "p.t1.t2", []string{ `
`, }, }, { ``, "p[title]", []string{ `
`, }, }, { ``, `address[title="foo"]`, []string{ ``, }, }, { ``, `address[title!="foo"]`, []string{ ``, ``, }, }, { ``, `[ title ~= foo ]`, []string{ `
`, }, }, { ``, `[title~="hello world"]`, []string{}, }, { `
`, `[lang|="en"]`, []string{ `
`, ``, }, }, { ``, `[title^="foo"]`, []string{ `
`, }, }, { ``, `[title$="bar"]`, []string{ `
`, }, }, { ``, `[title*="bar"]`, []string{ `
`, }, }, { `This text should be green.
This text should be green.
`, `p[class$=" "]`, []string{}, }, { `This text should be green.
This text should be green.
`, `p[class$=""]`, []string{}, }, { `This text should be green.
This text should be green.
`, `p[class^=" "]`, []string{}, }, { `This text should be green.
This text should be green.
`, `p[class^=""]`, []string{}, }, { `This text should be green.
This text should be green.
`, `p[class*=" "]`, []string{}, }, { `This text should be green.
This text should be green.
`, `p[class*=""]`, []string{}, }, { ``, `input[name=Sex][value=F]`, []string{ ``, }, }, { ``, ".t1:not(.t2)", []string{}, }, { `
some text and a span and another
`, `span:first-child`, []string{ `and a span`, }, }, { `a span and some text`, `span:last-child`, []string{ `a span`, }, }, { ``, `p:nth-of-type(2)`, []string{ `
`, }, }, { ``, `p:nth-last-of-type(2)`, []string{ ``, }, }, { ``, `p:last-of-type`, []string{ ``, }, }, { ``, `p:first-of-type`, []string{ ``, }, }, { `Hello
`,
`:empty`,
[]string{
``,
``,
``,
},
},
{
` `,
`div p`,
[]string{
`
`, `div table p`, []string{ `
`, }, }, { ``, `p ~ p`, []string{ `
`, ``, }, }, { ``, `p + p`, []string{ `
`, }, }, { ``, `li, p`, []string{ "
", "", "", }, }, { ``, `p +/*This is a comment*/ p`, []string{ `
`, }, }, { `Text block that wraps inner text and continues
`, `p:contains("that wraps")`, []string{ `Text block that wraps inner text and continues
`, }, }, { `Text block that wraps inner text and continues
`, `p:containsOwn("that wraps")`, []string{}, }, { `Text block that wraps inner text and continues
`, `:containsOwn("inner")`, []string{ `wraps inner text`, }, }, { `Text block that wraps inner text and continues
`, `p:containsOwn("block")`, []string{ `Text block that wraps inner text and continues
`, }, }, { `text content
text content
contents 1
contents 2
contents 2
contents 1
contents 2
contents 2
contents 2
`, }, }, { `contents 1
contents 2
contents 2
`, }, }, { `0123456789
abcdef
0123ABCD
`, `p:matches([\d])`, []string{ `0123456789
`, `0123ABCD
`, }, }, { `0123456789
abcdef
0123ABCD
`, `p:matches([a-z])`, []string{ `abcdef
`, }, }, { `0123456789
abcdef
0123ABCD
`, `p:matches([a-zA-Z])`, []string{ `abcdef
`, `0123ABCD
`, }, }, { `0123456789
abcdef
0123ABCD
`, `p:matches([^\d])`, []string{ `abcdef
`, `0123ABCD
`, }, }, { `0123456789
abcdef
0123ABCD
`, `p:matches(^(0|a))`, []string{ `0123456789
`, `abcdef
`, `0123ABCD
`, }, }, { `0123456789
abcdef
0123ABCD
`, `p:matches(^\d+$)`, []string{ `0123456789
`, }, }, { `0123456789
abcdef
0123ABCD
`, `p:not(:matches(^\d+$))`, []string{ `abcdef
`, `0123ABCD
`, }, }, { `0123456789
0123456789
`, `567`, }, }, { ``, `[href#=(fina)]:not([href#=(\/\/[^\/]+untrusted)])`, []string{ ``, ``, }, }, { ``, `[href#=(^https:\/\/[^\/]*\/?news)]`, []string{ ``, }, }, { ``, `:input`, []string{ ``, ``, ``, ``, ``, }, }, { ``, ":root", []string{ "", }, }, { ``, "*:root", []string{ "", }, }, { ``, "*:root:first-child", []string{}, }, { ``, "*:root:nth-child(1)", []string{}, }, { ``, "a:not(:root)", []string{ ``, }, }, } func TestSelectors(t *testing.T) { for _, test := range selectorTests { s, err := Compile(test.selector) if err != nil { t.Errorf("error compiling %q: %s", test.selector, err) continue } doc, err := html.Parse(strings.NewReader(test.HTML)) if err != nil { t.Errorf("error parsing %q: %s", test.HTML, err) continue } matches := s.MatchAll(doc) if len(matches) != len(test.results) { t.Errorf("selector %s wanted %d elements, got %d instead", test.selector, len(test.results), len(matches)) continue } for i, m := range matches { got := nodeString(m) if got != test.results[i] { t.Errorf("selector %s wanted %s, got %s instead", test.selector, test.results[i], got) } } firstMatch := s.MatchFirst(doc) if len(test.results) == 0 { if firstMatch != nil { t.Errorf("MatchFirst: selector %s want nil, got %s", test.selector, nodeString(firstMatch)) } } else { got := nodeString(firstMatch) if got != test.results[0] { t.Errorf("MatchFirst: selector %s want %s, got %s", test.selector, test.results[0], got) } } } }