repo_description: A curated list of awesome Go frameworks, libraries and software
repo_topics: awesome awesome-list go golang golang-library hacktoberfest
code: package main
  
  import (
  	"bytes"
  	"github.com/avelino/awesome-go/pkg/markdown"
  	"os"
  	"regexp"
  	"sort"
  	"strings"
  	"testing"
  
  	"github.com/PuerkitoBio/goquery"
  )
  
  var (
  	reContainsLink        = regexp.MustCompile(`\* \[.*\]\(.*\)`)
  	reOnlyLink            = regexp.MustCompile(`\* \[.*\]\([^()]*\)$`)
  	reLinkWithDescription = regexp.MustCompile(`\* \[.*\]\(.*\) - \S.*[\.\!]`)
  )
  
  func requireNoErr(t *testing.T, err error, msg string) {
  	// FIXME: replace to github.com/stretchr/testify
  	t.Helper()
  
  	if msg == "" {
  		msg = "unknown error"
  	}
  
  	if err != nil {
  		t.Fatalf("Received unexpected error [%s]: %+v", msg, err)
  	}
  }
  
  func goqueryFromReadme(t *testing.T) *goquery.Document {
  	t.Helper()
  
  	input, err := os.ReadFile(readmePath)
  	requireNoErr(t, err, "readme file should be exists")
  
  	html, err := markdown.ToHTML(input)
  	requireNoErr(t, err, "markdown should be rendered to html")
  
  	buf := bytes.NewBuffer(html)
  	doc, err := goquery.NewDocumentFromReader(buf)
  	requireNoErr(t, err, "html must be valid for goquery")
  
  	return doc
  }
  
  func TestAlpha(t *testing.T) {
  	doc := goqueryFromReadme(t)
  	doc.Find("body > ul").Each(func(i int, s *goquery.Selection) {
  		if i != 0 {
  			// skip content menu
  			// TODO: the sub items (with 3 hash marks `###`) are staying in
  			// the main list, not respecting the hierarchy and making it
  			// impossible to test the alphabetical order
  			testList(t, s)
  		}
  	})
  }
  
  func TestDuplicatedLinks(t *testing.T) {
  	doc := goqueryFromReadme(t)
  	links := make(map[string]bool, 0)
  	doc.Find("body li > a:first-child").Each(func(_ int, s *goquery.Selection) {
  		t.Run(s.Text(), func(t *testing.T) {
  			href, ok := s.Attr("href")
  			if !ok {
  				t.Error("expected to have href")
  			}
  			if links[href] {
  				t.Fatalf("duplicated link '%s'", href)
  			}
  			links[href] = true
  		})
  	})
  }
  
  // Test if an entry has description, it must be separated from link with ` - `
  func TestSeparator(t *testing.T) {
  	var matched, containsLink, noDescription bool
  	input, err := os.ReadFile(readmePath)
  	requireNoErr(t, err, "readme should be exists")
  
  	lines := strings.Split(string(input), "\n")
  	for _, line := range lines {
  		line = strings.Trim(line, " ")
  		containsLink = reContainsLink.MatchString(line)
  		if containsLink {
  			noDescription = reOnlyLink.MatchString(line)
  			if noDescription {
  				continue
  			}
  			matched = reLinkWithDescription.MatchString(line)
  			if !matched {
  				t.Errorf("expected entry to be in form of `* [link] - description.`, got '%s'", line)
  			}
  		}
  	}
  }
  
  func TestRenderIndex(t *testing.T) {
  	requireNoErr(t, mkdirAll(outDir), "output dir should exists")
  
  	err := renderIndex(readmePath, outIndexFile)
  	requireNoErr(t, err, "html should be rendered")
  }
  
  func testList(t *testing.T, list *goquery.Selection) {
  	list.Find("ul").Each(func(_ int, items *goquery.Selection) {
  		testList(t, items)
  		items.RemoveFiltered("ul")
  	})
  	t.Run(list.Prev().Text(), func(t *testing.T) {
  		checkAlphabeticOrder(t, list)
  	})
  }
  
  func checkAlphabeticOrder(t *testing.T, s *goquery.Selection) {
  	items := s.Find("li > a:first-child").Map(func(_ int, li *goquery.Selection) string {
  		return strings.ToLower(li.Text())
  	})
  	sorted := make([]string, len(items))
  	copy(sorted, items)
  	sort.Strings(sorted)
  	for k, item := range items {
  		if item != sorted[k] {
  			t.Errorf("expected '%s' but actual is '%s'", sorted[k], item)
  		}
  	}
  	if t.Failed() {
  		t.Logf("expected order is:\n%s", strings.Join(sorted, "\n"))
  	}
  }
  