Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix rule sorting by specificity #4

Merged
merged 2 commits into from Jun 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 5 additions & 7 deletions premailer/doc.go
@@ -1,22 +1,22 @@
// Package premailer is for inline styling.
//
//
// import (
// "fmt"
// "github.com/vanng822/go-premailer/premailer"
// "log"
// )
//
//
// func main() {
// prem := premailer.NewPremailerFromFile(inputFile, premailer.NewOptions())
// html, err := prem.Transform()
// if err != nil {
// log.Fatal(err)
// }
//
//
// fmt.Println(html)
// }
// // Input
//
//
// <html>
// <head>
// <title>Title</title>
Expand All @@ -30,7 +30,7 @@
// <p><strong>Yes!</strong></p>
// </body>
// </html>
//
//
// // Output
//
// <html>
Expand All @@ -43,5 +43,3 @@
// </body>
// </html>
package premailer


43 changes: 39 additions & 4 deletions premailer/premailer_test.go
@@ -1,8 +1,9 @@
package premailer

import (
"github.com/stretchr/testify/assert"
"testing"

"github.com/stretchr/testify/assert"
)

func TestBasicHTML(t *testing.T) {
Expand Down Expand Up @@ -316,7 +317,6 @@ func TestWithMediaAttribute(t *testing.T) {
assert.Contains(t, result_html, "line-height: 60px;")
assert.Contains(t, result_html, "padding-top: 0;")
assert.Contains(t, result_html, "padding-bottom: 5px")

}

func TestIndexOutOfRange(t *testing.T) {
Expand Down Expand Up @@ -367,5 +367,40 @@ func TestIndexOutOfRange(t *testing.T) {
assert.Contains(t, result_html, "line-height: 60px;")
assert.Contains(t, result_html, "padding-top: 0;")
assert.Contains(t, result_html, "padding-bottom: 5px")

}
}

func TestSpecificity(t *testing.T) {
html := `<html>
<head>
<title>Title</title>
<style type="text/css">
table.bar-chart td.bar-area {
padding: 10px;
}
table { width: 91%; }
table { width: 92%; }
table { width: 93%; }
table { width: 94%; }
table { width: 95%; }
table { width: 96%; }
table { width: 97%; }
table.bar-chart td {
padding: 5px;
}
</style>
</head>
<body>
<table class="bar-chart">
<tr><td>1</td></tr>
<tr><td class="bar-area">2</td></tr>
</table>
</body>
</html>`

p := NewPremailerFromString(html, NewOptions())
result_html, err := p.Transform()
assert.Nil(t, err)

assert.Contains(t, result_html, `<tr><td style="padding:5px">1</td></tr>`)
assert.Contains(t, result_html, `<tr><td class="bar-area" style="padding:10px">2</td></tr>`)
}
4 changes: 2 additions & 2 deletions premailer/selector_compatible_test.go
Expand Up @@ -96,7 +96,7 @@ func TestNotSupportedSelectors(t *testing.T) {

pr := premailer{}
pr.doc = doc

notSupportedSelectors := []string{
"input:checked",
"input:disabled",
Expand All @@ -111,4 +111,4 @@ func TestNotSupportedSelectors(t *testing.T) {
pr.doc.Find(selector)
})
}
}
}
25 changes: 14 additions & 11 deletions premailer/specificity.go
@@ -1,28 +1,28 @@
package premailer

import (
"strings"
"regexp"
)
"strings"
)

// https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
// https://developer.mozilla.org/en-US/docs/Web/CSS/Reference#Selectors

type specificity struct {
important int
idCount int
classCount int
typeCount int
attrCount int
important int
idCount int
classCount int
typeCount int
attrCount int
ruleSetIndex int
ruleIndex int
ruleIndex int
}

func (s *specificity) importantOrders() []int {
return []int{s.important, s.idCount,
s.classCount, s.attrCount,
s.typeCount, s.ruleSetIndex,
s.ruleIndex}
s.classCount, s.attrCount,
s.typeCount, s.ruleSetIndex,
s.ruleIndex}
}

var _type_selector_regex = regexp.MustCompile("(^|\\s)\\w")
Expand Down Expand Up @@ -60,6 +60,9 @@ func (bs bySpecificity) Less(i, j int) bool {
if v < jorders[n] {
return true
}
if v > jorders[n] {
return false
}
}
return false
}
52 changes: 37 additions & 15 deletions premailer/specificity_test.go
@@ -1,9 +1,10 @@
package premailer

import (
"sort"
"testing"

"github.com/stretchr/testify/assert"
"sort"
)

func TestSpecificitySelectorType(t *testing.T) {
Expand All @@ -26,9 +27,8 @@ func TestSpecificitySelectorAttr(t *testing.T) {
assert.Equal(t, expected, spec.importantOrders())
}


func TestSpecificitySelectorId(t *testing.T) {
// id
// id
spec := makeSpecificity(0, 3, 104, "#example")
expected := []int{0, 1, 0, 0, 0, 3, 104}
assert.Equal(t, expected, spec.importantOrders())
Expand All @@ -47,16 +47,16 @@ func TestSpecificitySort(t *testing.T) {
undertest[2].specificity = specificity2
specificity3 := makeSpecificity(0, 3, 104, "#example")
undertest[3].specificity = specificity3
// expected order

// expected order
/*
expected3 := []int{0, 1, 0, 0, 0, 3, 104}
expected0 := []int{1, 0, 0, 0, 1, 2, 100}
expected2 := []int{1, 0, 0, 1, 1, 3, 103}
expected1 := []int{1, 0, 1, 0, 1, 2, 102}
expected3 := []int{0, 1, 0, 0, 0, 3, 104}
expected0 := []int{1, 0, 0, 0, 1, 2, 100}
expected2 := []int{1, 0, 0, 1, 1, 3, 103}
expected1 := []int{1, 0, 1, 0, 1, 2, 102}
*/
sort.Sort(bySpecificity(undertest))

assert.Equal(t, specificity3, undertest[0].specificity)
assert.Equal(t, specificity0, undertest[1].specificity)
assert.Equal(t, specificity2, undertest[2].specificity)
Expand All @@ -72,9 +72,9 @@ func TestSpecificitySortRuleSetIndex(t *testing.T) {
undertest[0].specificity = specificity0
specificity1 := makeSpecificity(1, 1, 102, "table")
undertest[1].specificity = specificity1

sort.Sort(bySpecificity(undertest))

assert.Equal(t, specificity1, undertest[0].specificity)
assert.Equal(t, specificity0, undertest[1].specificity)
}
Expand All @@ -88,9 +88,31 @@ func TestSpecificitySortRuleIndex(t *testing.T) {
undertest[0].specificity = specificity0
specificity1 := makeSpecificity(1, 1, 100, "table")
undertest[1].specificity = specificity1

sort.Sort(bySpecificity(undertest))

assert.Equal(t, specificity1, undertest[0].specificity)
assert.Equal(t, specificity0, undertest[1].specificity)
}
}

func TestSpecificitySortLongArray(t *testing.T) {
// It has to be longer than 6 due to internal implementation of sort.Sort(),
rules := []*styleRule{
&styleRule{specificity: makeSpecificity(0, 0, 1, "table.padded")},
&styleRule{specificity: makeSpecificity(0, 0, 2, "table.padded")},
&styleRule{specificity: makeSpecificity(0, 0, 3, "table.padded")},
&styleRule{specificity: makeSpecificity(0, 0, 4, "table.padded")},
&styleRule{specificity: makeSpecificity(0, 0, 5, "table.padded")},
&styleRule{specificity: makeSpecificity(0, 0, 6, "table.padded")},
&styleRule{specificity: makeSpecificity(0, 0, 11, "table")},
}

sort.Sort(bySpecificity(rules))

ruleIndices := make([]int, len(rules))
for i := range rules {
ruleIndices[i] = rules[i].specificity.ruleIndex
}
expectedRuleIndices := []int{11, 1, 2, 3, 4, 5, 6}
assert.Equal(t, expectedRuleIndices, ruleIndices)
}
2 changes: 1 addition & 1 deletion premailer/style_rule.go
Expand Up @@ -8,4 +8,4 @@ type styleRule struct {
specificity *specificity
selector string
styles map[string]*css.CSSStyleDeclaration
}
}
2 changes: 1 addition & 1 deletion premailer/util.go
Expand Up @@ -21,4 +21,4 @@ func makeRuleImportant(rule *css.CSSRule) string {
s.Important = 1
}
return rule.Style.Text()
}
}