소스 검색

Add more unit tests for template functions

Frédéric Guillot 7 년 전
부모
커밋
f244df6293
5개의 변경된 파일130개의 추가작업 그리고 123개의 파일을 삭제
  1. 0 22
      template/dict.go
  2. 0 42
      template/dict_test.go
  3. 2 0
      template/engine.go
  4. 47 24
      template/functions.go
  5. 81 35
      template/functions_test.go

+ 0 - 22
template/dict.go

@@ -1,22 +0,0 @@
-// Copyright 2018 Frédéric Guillot. All rights reserved.
-// Use of this source code is governed by the Apache 2.0
-// license that can be found in the LICENSE file.
-
-package template // import "miniflux.app/template"
-
-import "fmt"
-
-func dict(values ...interface{}) (map[string]interface{}, error) {
-	if len(values)%2 != 0 {
-		return nil, fmt.Errorf("Dict expects an even number of arguments")
-	}
-	dict := make(map[string]interface{}, len(values)/2)
-	for i := 0; i < len(values); i += 2 {
-		key, ok := values[i].(string)
-		if !ok {
-			return nil, fmt.Errorf("Dict keys must be strings")
-		}
-		dict[key] = values[i+1]
-	}
-	return dict, nil
-}

+ 0 - 42
template/dict_test.go

@@ -1,42 +0,0 @@
-// Copyright 2018 Frédéric Guillot. All rights reserved.
-// Use of this source code is governed by the Apache 2.0
-// license that can be found in the LICENSE file.
-
-package template // import "miniflux.app/template"
-
-import (
-	"testing"
-)
-
-func TestDict(t *testing.T) {
-	d, err := dict("k1", "v1", "k2", "v2")
-	if err != nil {
-		t.Fatalf(`The dict should be valid: %v`, err)
-	}
-
-	if value, found := d["k1"]; found {
-		if value != "v1" {
-			t.Fatalf(`Incorrect value for k1: %q`, value)
-		}
-	}
-
-	if value, found := d["k2"]; found {
-		if value != "v2" {
-			t.Fatalf(`Incorrect value for k2: %q`, value)
-		}
-	}
-}
-
-func TestDictWithIncorrectNumberOfPairs(t *testing.T) {
-	_, err := dict("k1", "v1", "k2")
-	if err == nil {
-		t.Fatalf(`The dict should not be valid because the number of keys/values pairs are incorrect`)
-	}
-}
-
-func TestDictWithInvalidKey(t *testing.T) {
-	_, err := dict(1, "v1")
-	if err == nil {
-		t.Fatalf(`The dict should not be valid because the key is not a string`)
-	}
-}

+ 2 - 0
template/engine.go

@@ -44,6 +44,8 @@ func (e *Engine) Render(name, language string, data interface{}) []byte {
 	}
 
 	lang := e.translator.GetLanguage(language)
+
+	// Functions that need to be declared at runtime.
 	tpl.Funcs(template.FuncMap{
 		"elapsed": func(timezone string, t time.Time) string {
 			return elapsedTime(lang, timezone, t)

+ 47 - 24
template/functions.go

@@ -5,6 +5,7 @@
 package template // import "miniflux.app/template"
 
 import (
+	"fmt"
 	"html/template"
 	"net/mail"
 	"strings"
@@ -23,8 +24,13 @@ type funcMap struct {
 	router *mux.Router
 }
 
+// Map returns a map of template functions that are compiled during template parsing.
 func (f *funcMap) Map() template.FuncMap {
 	return template.FuncMap{
+		"dict":     dict,
+		"hasKey":   hasKey,
+		"truncate": truncate,
+		"isEmail":  isEmail,
 		"baseURL": func() string {
 			return f.cfg.BaseURL()
 		},
@@ -34,12 +40,6 @@ func (f *funcMap) Map() template.FuncMap {
 		"hasOAuth2Provider": func(provider string) bool {
 			return f.cfg.OAuth2Provider() == provider
 		},
-		"hasKey": func(dict map[string]string, key string) bool {
-			if value, found := dict[key]; found {
-				return value != ""
-			}
-			return false
-		},
 		"route": func(name string, args ...interface{}) string {
 			return route.Path(f.router, name, args...)
 		},
@@ -61,13 +61,6 @@ func (f *funcMap) Map() template.FuncMap {
 		"domain": func(websiteURL string) string {
 			return url.Domain(websiteURL)
 		},
-		"isEmail": func(str string) bool {
-			_, err := mail.ParseAddress(str)
-			if err != nil {
-				return false
-			}
-			return true
-		},
 		"hasPrefix": func(str, prefix string) bool {
 			return strings.HasPrefix(str, prefix)
 		},
@@ -77,17 +70,6 @@ func (f *funcMap) Map() template.FuncMap {
 		"isodate": func(ts time.Time) string {
 			return ts.Format("2006-01-02 15:04:05")
 		},
-		"dict": dict,
-		"truncate": func(str string, max int) string {
-			runes := 0
-			for i := range str {
-				runes++
-				if runes > max {
-					return str[:i] + "…"
-				}
-			}
-			return str
-		},
 		"theme_color": func(theme string) string {
 			return model.ThemeColor(theme)
 		},
@@ -108,3 +90,44 @@ func (f *funcMap) Map() template.FuncMap {
 func newFuncMap(cfg *config.Config, router *mux.Router) *funcMap {
 	return &funcMap{cfg, router}
 }
+
+func dict(values ...interface{}) (map[string]interface{}, error) {
+	if len(values)%2 != 0 {
+		return nil, fmt.Errorf("dict expects an even number of arguments")
+	}
+	dict := make(map[string]interface{}, len(values)/2)
+	for i := 0; i < len(values); i += 2 {
+		key, ok := values[i].(string)
+		if !ok {
+			return nil, fmt.Errorf("dict keys must be strings")
+		}
+		dict[key] = values[i+1]
+	}
+	return dict, nil
+}
+
+func hasKey(dict map[string]string, key string) bool {
+	if value, found := dict[key]; found {
+		return value != ""
+	}
+	return false
+}
+
+func truncate(str string, max int) string {
+	runes := 0
+	for i := range str {
+		runes++
+		if runes > max {
+			return str[:i] + "…"
+		}
+	}
+	return str
+}
+
+func isEmail(str string) bool {
+	_, err := mail.ParseAddress(str)
+	if err != nil {
+		return false
+	}
+	return true
+}

+ 81 - 35
template/functions_test.go

@@ -8,41 +8,87 @@ import (
 	"testing"
 )
 
-func TestTruncate(t *testing.T) {
-	fm := funcMap{}
-	if f, ok := fm.Map()["truncate"]; ok {
-		if truncate := f.(func(str string, max int) string); ok {
-			shortEnglishText := "Short text"
-			shortUnicodeText := "Короткий текст"
-
-			// edge case
-			if truncate(shortEnglishText, len(shortEnglishText)) != shortEnglishText {
-				t.Fatal("Invalid truncation")
-			}
-			// real case
-			if truncate(shortEnglishText, 25) != shortEnglishText {
-				t.Fatal("Invalid truncation")
-			}
-			if truncate(shortUnicodeText, len(shortUnicodeText)) != shortUnicodeText {
-				t.Fatal("Invalid truncation")
-			}
-			if truncate(shortUnicodeText, 25) != shortUnicodeText {
-				t.Fatal("Invalid truncation")
-			}
-
-			longEnglishText := "This is really pretty long English text"
-			longRussianText := "Это реально очень длинный русский текст"
-
-			if truncate(longEnglishText, 25) != "This is really pretty lon…" {
-				t.Fatal("Invalid truncation")
-			}
-			if truncate(longRussianText, 25) != "Это реально очень длинный…" {
-				t.Fatal("Invalid truncation")
-			}
-		} else {
-			t.Fatal("Type assetion for this func is failed, check func, maybe it was changed")
+func TestDict(t *testing.T) {
+	d, err := dict("k1", "v1", "k2", "v2")
+	if err != nil {
+		t.Fatalf(`The dict should be valid: %v`, err)
+	}
+
+	if value, found := d["k1"]; found {
+		if value != "v1" {
+			t.Fatalf(`Unexpected value for k1: got %q`, value)
+		}
+	}
+
+	if value, found := d["k2"]; found {
+		if value != "v2" {
+			t.Fatalf(`Unexpected value for k2: got %q`, value)
 		}
-	} else {
-		t.Fatal("There is no such function in this map, check key, maybe it was changed")
+	}
+}
+
+func TestDictWithInvalidNumberOfArguments(t *testing.T) {
+	_, err := dict("k1")
+	if err == nil {
+		t.Fatal(`An error should be returned if the number of arguments are not even`)
+	}
+}
+
+func TestDictWithInvalidMap(t *testing.T) {
+	_, err := dict(1, 2)
+	if err == nil {
+		t.Fatal(`An error should be returned if the dict keys are not string`)
+	}
+}
+
+func TestHasKey(t *testing.T) {
+	input := map[string]string{"k": "v"}
+
+	if !hasKey(input, "k") {
+		t.Fatal(`This key exists in the map and should returns true`)
+	}
+
+	if hasKey(input, "missing") {
+		t.Fatal(`This key doesn't exists in the given map and should returns false`)
+	}
+}
+
+func TestTruncateWithShortTexts(t *testing.T) {
+	scenarios := []string{"Short text", "Короткий текст"}
+
+	for _, input := range scenarios {
+		result := truncate(input, 25)
+		if result != input {
+			t.Fatalf(`Unexpected output, got %q instead of %q`, result, input)
+		}
+
+		result = truncate(input, len(input))
+		if result != input {
+			t.Fatalf(`Unexpected output, got %q instead of %q`, result, input)
+		}
+	}
+}
+
+func TestTruncateWithLongTexts(t *testing.T) {
+	scenarios := map[string]string{
+		"This is a really pretty long English text": "This is a really pretty l…",
+		"Это реально очень длинный русский текст":   "Это реально очень длинный…",
+	}
+
+	for input, expected := range scenarios {
+		result := truncate(input, 25)
+		if result != expected {
+			t.Fatalf(`Unexpected output, got %q instead of %q`, result, expected)
+		}
+	}
+}
+
+func TestIsEmail(t *testing.T) {
+	if !isEmail("user@domain.tld") {
+		t.Fatal(`This email is valid and should returns true`)
+	}
+
+	if isEmail("invalid") {
+		t.Fatal(`This email is not valid and should returns false`)
 	}
 }