shithub: mycel

ref: cb083ed612f90d648a7a5c27b8e155beef6c7bfb
dir: /style/stylesheets_test.go/

View raw version
package style

import (
	"fmt"
	"github.com/mjl-/duit"
	"github.com/psilva261/mycel/logger"
	"golang.org/x/net/html"
	"strings"
	"testing"
)

func init() {
	log.Debug = true
}

func TestColorHex(t *testing.T) {
	tri, ok := colorHex("red")
	if !ok {
		t.Fail()
	}

	hri, ok := colorHex("#ff0000")
	if !ok {
		t.Fail()
	}

	if tri != hri {
		t.Fatalf("tri=%x hri=%x", tri, hri)
	}

	if _, ok := colorHex("rgb(1,2)"); ok {
		t.Fail()
	}
}

func TestColorHex3(t *testing.T) {
	c, ok := colorHex("#fff")
	if !ok {
		t.Fail()
	}

	if uint32(c) != 0xffffffff {
		t.Errorf("c=%x", c)
	}
}

func TestFetchNodeRules(t *testing.T) {
	data := `<body>
      		<h2 id="foo">a header</h2>
     		<h2 id="bar">another header</h2>
   		<p>Some text <b>in bold</b></p>
    	</body>`
	doc, err := html.Parse(strings.NewReader(data))
	if err != nil {
		t.Fail()
	}
	css := AddOnCSS + `
b {
	width: 100px!important;
}

@media only screen and (max-width: 600px) {
  body {
    background-color: lightblue;
  }
}
	`
	for _, w := range []int{400, 800} {
		MediaValues["width"] = fmt.Sprintf("%vpx", w)
		t.Logf("w=%v", w)
		m, _, err := FetchNodeRules(doc, css)
		if err != nil {
			t.Fail()
		}
		t.Logf("m=%+v", m)

		var b *html.Node
		var body *html.Node

		var f func(n *html.Node)
		f = func(n *html.Node) {
			switch n.Data {
			case "b":
				b = n
			case "body":
				body = n
			}
			for c := n.FirstChild; c != nil; c = c.NextSibling {
				f(c)
			}
		}

		f(doc)

		importantFound := false
		for _, r := range m[b] {
			if r.Declarations[0].Important {
				importantFound = true
			}
			for _, d := range r.Declarations {
				if d.Specificity[0] != 0 || d.Specificity[1] != 0 || d.Specificity[2] != 1 {
					t.Fail()
				}
			}
		}
		if !importantFound {
			t.Fail()
		}

		if w == 400 {
			_ = m[body][0]
			if v := m[body][0].Declarations[0].Val; v != "lightblue" {
				t.Fatalf("%v", v)
			}
			t.Logf("%v", m[body][0])
		} else {
			if _, ok := m[body]; ok {
				t.Fatalf("body ok")
			}
		}
	}
}

func TestFetchNodeRules2(t *testing.T) {
	data := `<h2 id="outer">
				<h2 id="sp1">[</h2>
				<h2 id="sp2">]</h2>
			</h2>`
	doc, err := html.Parse(strings.NewReader(data))
	if err != nil {
		t.Fail()
	}
	m, _, err := FetchNodeRules(doc, AddOnCSS)
	if err != nil {
		t.Fail()
	}
	t.Logf("m=%+v", m)

	var outer *html.Node
	var sp1 *html.Node
	var sp2 *html.Node

	var f func(n *html.Node)
	f = func(n *html.Node) {
		if len(n.Attr) == 1 {
			switch n.Attr[0].Val {
			case "outer":
				outer = n
			case "sp1":
				sp1 = n
			case "sp2":
				sp2 = n
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)

	/*t.Logf("outer=%+v", outer)
	t.Logf("sp1=%+v", sp1)
	t.Logf("sp2=%+v", sp2)*/

	for _, n := range []*html.Node{outer, sp1, sp2} {
		_, ok := m[n]
		if !ok {
			t.Errorf("not found: %+v", n)
		} else {
			t.Logf("success: %+v", n)
		}
	}
}

func TestFetchNodeMap(t *testing.T) {
	data := `<p>
      		<h2 id="foo">a header</h2>
     		<h2 id="bar">another header</h2>
   		<p>Some text <b>in bold</b></p>
    	</p>`
	doc, err := html.Parse(strings.NewReader(data))
	if err != nil {
		t.Fail()
	}
	m, err := FetchNodeMap(doc, AddOnCSS)
	if err != nil {
		t.Fail()
	}
	t.Logf("m=%+v", m)
}

func TestMergeNodeMaps(t *testing.T) {
	nodeMap := make(map[*html.Node]Map)
	data := `<p>
      		<a class="link" href="http://example.com">Test</a>
    	</p>`
	doc, err := html.Parse(strings.NewReader(data))
	if err != nil {
		t.Fail()
	}
	a := grep(doc, "a")
	m, err := FetchNodeMap(doc, AddOnCSS)
	if err != nil {
		t.Fail()
	}
	MergeNodeMaps(nodeMap, m)
	if nodeMap[a].Css("color") != "blue" {
		t.Fatalf("%v", nodeMap[a])
	}
	m2, err := FetchNodeMap(doc, `.link { color: red; }`)
	if err != nil {
		t.Fail()
	}
	MergeNodeMaps(nodeMap, m2)
	if nodeMap[a].Css("color") != "red" {
		t.Fatalf("%v", nodeMap[a])
	}
}

func TestNewMapStyle(t *testing.T) {
	htms := []string{
		`<h2 style="color: green;">a header</h2>`,
		`<h2 style="color: green">a header</h2>`,
	}
	for _, htm := range htms {
		doc, err := html.Parse(strings.NewReader(htm))
		if err != nil {
			t.Fail()
		}

		h2 := grep(doc, "h2")
		m := NewMap(h2)

		if m.Declarations["color"].Val != "green" {
			t.Errorf("%+v", m)
		}
	}
}

func grep(nn *html.Node, tag string) *html.Node {
	var f func(n *html.Node) *html.Node
	f = func(n *html.Node) *html.Node {
		if n.Data == tag {
			return n
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			if m := f(c); m != nil {
				return m
			}
		}
		return nil
	}
	return f(nn)
}

func TestSmaller(t *testing.T) {
	d := Declaration{
		Important: false,
	}
	dd := Declaration{
		Important: true,
	}
	if !smaller(d, dd) {
		t.Fail()
	}
}

func TestApplyChildStyleInherit(t *testing.T) {
	parent := Map{
		Declarations: make(map[string]Declaration),
	}
	parent.Declarations["height"] = Declaration{
		Prop: "height",
		Val:  "80px",
	}
	child := Map{
		Declarations: make(map[string]Declaration),
	}

	res := parent.ApplyChildStyle(child, true)
	if v := res.Declarations["height"].Val; v != "80px" {
		t.Fatalf(v)
	}
}

func TestApplyChildStyleInherit2(t *testing.T) {
	parent := Map{
		Declarations: make(map[string]Declaration),
	}
	child := Map{
		Declarations: make(map[string]Declaration),
	}
	parent.Declarations["font-size"] = Declaration{
		Prop: "font-size",
		Val:  "12pt",
	}
	child.Declarations["font-size"] = Declaration{
		Prop: "font-size",
		Val:  "inherit",
	}

	res := parent.ApplyChildStyle(child, true)
	if v := res.Declarations["font-size"].Val; v != "12pt" {
		t.Fatalf(v)
	}
}

func TestApplyChildStyleInherit3(t *testing.T) {
	parent := Map{
		Declarations: make(map[string]Declaration),
	}
	child := Map{
		Declarations: make(map[string]Declaration),
	}
	parent.Declarations["font-size"] = Declaration{
		Prop: "font-size",
		Val:  "12pt",
	}
	child.Declarations["font-size"] = Declaration{
		Prop: "font-size",
		Val:  "13pt",
	}

	res := parent.ApplyChildStyle(child, true)
	if v := res.Declarations["font-size"].Val; v != "13pt" {
		t.Fatalf(v)
	}
}

func TestApplyChildStyleInherit4(t *testing.T) {
	parent := Map{
		Declarations: make(map[string]Declaration),
	}
	child := Map{
		Declarations: make(map[string]Declaration),
	}
	parent.Declarations["font-size"] = Declaration{
		Prop:        "font-size",
		Val:         "12pt",
		Specificity: [3]int{0, 2, 0},
	}
	child.Declarations["font-size"] = Declaration{
		Prop:        "font-size",
		Val:         "13pt",
		Specificity: [3]int{0, 1, 0},
	}

	res := parent.ApplyChildStyle(child, true)
	if v := res.Declarations["font-size"].Val; v != "12pt" {
		t.Fatalf(v)
	}
}

func TestCalc(t *testing.T) {
	tests := map[string]float64{
		"calc(1px+2px)":         3.0,
		"calc(1px + 2px)":       3.0,
		"calc(1em+2px)":         13.0,
		"calc(1em+(2px-1px))":   12.0,
		"calc(1em+(2px-1.5px))": 11.5,
	}
	for x, px := range tests {
		f, _, err := length(nil, x)
		if err != nil {
			t.Fatalf("%v: %v", x, err)
		}
		if f != px {
			t.Fatalf("expected %v but got %v", px, f)
		}
	}
}

func TestCalc2(t *testing.T) {
	fails := []string{
		"calc(a+2px)",
		"calc(if(1)2)",
		"calc(quit)",
		"calc(1;)",
		"calc()",
		"calc(" + strings.Repeat("1", 51) + ")",
	}
	for _, x := range fails {
		_, _, err := length(nil, x)
		if err == nil {
			t.Fatalf("%v: %v", x, err)
		}
	}
}

func TestLength(t *testing.T) {
	lpx := map[string]float64{
		"auto":    0.0,
		"inherit": 0.0,
		"17px":    17.0,
		"10em":    110.0,
		"10ex":    110.0,
		"10vw":    128.0,
		"10vh":    108.0,
		"10%":     0,
		"101.6mm": 400,
	}
	for l, px := range lpx {
		f, _, err := length(nil, l)
		if err != nil {
			t.Fatalf("%v: %v", l, err)
		}
		if f != px {
			t.Fatalf("expected %v but got %v", px, f)
		}
	}
}

func TestTlbr(tt *testing.T) {
	cases := map[string]duit.Space{
		"1px 2px 3px 4px": duit.Space{1, 2, 3, 4},
		"1px 2px 3px":     duit.Space{1, 2, 3, 2},
		"1px 2px":         duit.Space{1, 2, 1, 2},
		"1px":             duit.Space{1, 1, 1, 1},
	}
	for v, exp := range cases {
		m := Map{
			Declarations: make(map[string]Declaration),
		}
		m.Declarations["margin"] = Declaration{
			Prop: "margin",
			Val:  v,
		}
		s, err := m.Tlbr("margin")
		if err != nil {
			tt.Errorf("%v", s)
		}
		if s != exp {
			tt.Errorf("%v: %v", s, exp)
		}
	}
}

func TestCssVars(t *testing.T) {
	data := `<body>
      		<h2 id="foo">a header</h2>
     		<h2 id="bar">another header</h2>
   		<p>Some text <b>in bold</b></p>
    	</body>`
	doc, err := html.Parse(strings.NewReader(data))
	if err != nil {
		t.Fail()
	}
	css := AddOnCSS + `
:root {
	--emph: red;
	--h: 10px;
}

b {
	color: var(--emph);
}
	`

	_, rv, err := FetchNodeRules(doc, css)
	if err != nil {
		t.Fail()
	}

	if len(rv) != 2 || rv["--emph"] != "red" || rv["--h"] != "10px" {
		t.Fail()
	}

	var b *html.Node
	var f func(n *html.Node)
	f = func(n *html.Node) {
		if n.Data == "b" {
			b = n
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)

	nm, err := FetchNodeMap(doc, css)
	if err != nil {
		t.Fail()
	}
	d := nm[b]
	t.Logf("d=%+v", d)
	if d.Declarations["color"].Val != "red" {
		t.Fatalf("%+v", d.Declarations)
	}
}