package htmlcheck import ( "fmt" "regexp" "sort" "strings" "github.com/PuerkitoBio/goquery" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" ) // RunTests will run all tests on an HTML string func RunTests(html string) (Response, error) { s := Response{} s.Warnings = []Warning{} if platforms, err := Platforms(); err == nil { s.Platforms = platforms } s.Total = Total{} // crude way to determine whether the HTML contains a
structure // or whether it's just plain HTML content re := regexp.MustCompile(`(?im)`) nodeMatch := "body *, script" if re.MatchString(html) { nodeMatch = "*:not(html):not(head):not(meta), script" } reader := strings.NewReader(html) // Load the HTML document doc, err := goquery.NewDocumentFromReader(reader) if err != nil { return s, err } // calculate the number of nodes in HTML s.Total.Nodes = len(doc.Find(nodeMatch).Nodes) if err := loadJSONData(); err != nil { return s, err } // HTML tests htmlResults, totalTests, err := runHTMLTests(html) if err != nil { return s, err } s.Total.Tests = s.Total.Tests + totalTests // add html test totals s.Warnings = append(s.Warnings, htmlResults...) // CSS tests cssResults, totalTests, err := runCSSTests(html) if err != nil { return s, err } s.Total.Tests = s.Total.Tests + totalTests // add css test totals s.Warnings = append(s.Warnings, cssResults...) // calculate total score var partial, unsupported float32 partial = 0 unsupported = 0 for _, w := range s.Warnings { if w.Score.Found == 0 { continue } // supported is calculated by subtracting partial and unsupported from 100% if w.Score.Partial > 0 { weighted := w.Score.Partial * float32(w.Score.Found) / float32(s.Total.Nodes) if weighted > partial { partial = weighted } } if w.Score.Unsupported > 0 { weighted := w.Score.Unsupported * float32(w.Score.Found) / float32(s.Total.Nodes) if weighted > unsupported { unsupported = weighted } } } s.Total.Supported = 100 - partial - unsupported s.Total.Partial = partial s.Total.Unsupported = unsupported // sort slice to get lowest scores first sort.Slice(s.Warnings, func(i, j int) bool { return (s.Warnings[i].Score.Unsupported+s.Warnings[i].Score.Partial)*float32(s.Warnings[i].Score.Found)/float32(s.Total.Nodes) > (s.Warnings[j].Score.Unsupported+s.Warnings[j].Score.Partial)*float32(s.Warnings[j].Score.Found)/float32(s.Total.Nodes) }) return s, nil } // Test returns a test func (c CanIEmail) getTest(k string) (Warning, error) { warning := Warning{} exists := false found := JSONResult{} for _, r := range cie.Data { if r.Slug == k { found = r exists = true break } } if !exists { return warning, fmt.Errorf("%s does not exist", k) } warning.Slug = found.Slug warning.Title = found.Title warning.Description = mdToHTML(found.Description) warning.Category = found.Category warning.URL = found.URL warning.Tags = found.Tags // warning.Keywords = found.Keywords // warning.Notes = found.Notes warning.NotesByNumber = make(map[string]string, len(found.NotesByNumber)) for nr, note := range found.NotesByNumber { warning.NotesByNumber[nr] = mdToHTML(note) } warning.Results = []Result{} var y, n, p float32 for family, stats := range found.Stats { if len(LimitFamilies) != 0 && !inArray(family, LimitFamilies) { continue } for platform, clients := range stats.(map[string]interface{}) { if len(LimitPlatforms) != 0 && !inArray(platform, LimitPlatforms) { continue } for version, support := range clients.(map[string]interface{}) { s := Result{} s.Name = fmt.Sprintf("%s %s (%s)", c.NiceNames.Family[family], c.NiceNames.Platform[platform], version) s.Family = family s.Platform = platform s.Version = version if support == "y" { y++ s.Support = "yes" } else if support == "n" { n++ s.Support = "no" } else { p++ s.Support = "partial" noteIDS := noteMatch.FindStringSubmatch(fmt.Sprintf("%s", support)) for _, id := range noteIDS { s.NoteNumber = id } } warning.Results = append(warning.Results, s) } } } total := y + n + p warning.Score.Supported = y / total * 100 warning.Score.Unsupported = n / total * 100 warning.Score.Partial = p / total * 100 return warning, nil } func inArray(n string, h []string) bool { n = strings.ToLower(n) for _, v := range h { if strings.ToLower(v) == n { return true } } return false } // Convert markdown to HTML, stripping&
func mdToHTML(str string) string { md := []byte(str) // create markdown parser with extensions extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock // extensions := parser.NoExtensions p := parser.NewWithExtensions(extensions) doc := p.Parse(md) // create HTML renderer with extensions htmlFlags := html.CommonFlags | html.HrefTargetBlank opts := html.RendererOptions{Flags: htmlFlags} renderer := html.NewRenderer(opts) return strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(string(markdown.Render(doc, renderer))), ""), "
") }