1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
package approvaltests
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"encoding/xml"
"github.com/approvals/go-approval-tests/reporters"
"github.com/approvals/go-approval-tests/utils"
"reflect"
)
var (
defaultReporter = reporters.NewDiffReporter()
defaultFrontLoadedReporter = reporters.NewFrontLoadedReporter()
)
// Failable is an interface wrapper around testing.T
type Failable interface {
Fail()
}
// VerifyWithExtension Example:
// VerifyWithExtension(t, strings.NewReader("Hello"), ".txt")
func VerifyWithExtension(t Failable, reader io.Reader, extWithDot string) error {
namer, err := getApprovalName()
if err != nil {
return err
}
reporter := getReporter()
err = namer.compare(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot), reader)
if err != nil {
reporter.Report(namer.getApprovalFile(extWithDot), namer.getReceivedFile(extWithDot))
t.Fail()
} else {
os.Remove(namer.getReceivedFile(extWithDot))
}
return err
}
// Verify Example:
// Verify(t, strings.NewReader("Hello"))
func Verify(t Failable, reader io.Reader) error {
return VerifyWithExtension(t, reader, ".txt")
}
// VerifyString Example:
// VerifyString(t, "Hello")
func VerifyString(t Failable, s string) error {
reader := strings.NewReader(s)
return Verify(t, reader)
}
// VerifyXMLStruct Example:
// VerifyXMLStruct(t, xml)
func VerifyXMLStruct(t Failable, obj interface{}) error {
xmlb, err := xml.MarshalIndent(obj, "", " ")
if err != nil {
tip := ""
if reflect.TypeOf(obj).Name() == "" {
tip = "when using anonymous types be sure to include\n XMLName xml.Name `xml:\"Your_Name_Here\"`\n"
}
message := fmt.Sprintf("error while pretty printing XML\n%verror:\n %v\nXML:\n %v\n", tip, err, obj)
return VerifyWithExtension(t, strings.NewReader(message), ".xml")
}
return VerifyWithExtension(t, bytes.NewReader(xmlb), ".xml")
}
// VerifyXMLBytes Example:
// VerifyXMLBytes(t, []byte("<Test/>"))
func VerifyXMLBytes(t Failable, bs []byte) error {
type node struct {
Attr []xml.Attr
XMLName xml.Name
Children []node `xml:",any"`
Text string `xml:",chardata"`
}
x := node{}
err := xml.Unmarshal(bs, &x)
if err != nil {
message := fmt.Sprintf("error while parsing XML\nerror:\n %s\nXML:\n %s\n", err, string(bs))
return VerifyWithExtension(t, strings.NewReader(message), ".xml")
}
return VerifyXMLStruct(t, x)
}
// VerifyJSONStruct Example:
// VerifyJSONStruct(t, json)
func VerifyJSONStruct(t Failable, obj interface{}) error {
jsonb, err := json.MarshalIndent(obj, "", " ")
if err != nil {
message := fmt.Sprintf("error while pretty printing JSON\nerror:\n %s\nJSON:\n %s\n", err, obj)
return VerifyWithExtension(t, strings.NewReader(message), ".json")
}
return VerifyWithExtension(t, bytes.NewReader(jsonb), ".json")
}
// VerifyJSONBytes Example:
// VerifyJSONBytes(t, []byte("{ \"Greeting\": \"Hello\" }"))
func VerifyJSONBytes(t Failable, bs []byte) error {
var obj map[string]interface{}
err := json.Unmarshal(bs, &obj)
if err != nil {
message := fmt.Sprintf("error while parsing JSON\nerror:\n %s\nJSON:\n %s\n", err, string(bs))
return VerifyWithExtension(t, strings.NewReader(message), ".json")
}
return VerifyJSONStruct(t, obj)
}
// VerifyMap Example:
// VerifyMap(t, map[string][string] { "dog": "bark" })
func VerifyMap(t Failable, m interface{}) error {
outputText := utils.PrintMap(m)
return VerifyString(t, outputText)
}
// VerifyArray Example:
// VerifyArray(t, []string{"dog", "cat"})
func VerifyArray(t Failable, array interface{}) error {
outputText := utils.PrintArray(array)
return VerifyString(t, outputText)
}
// VerifyAll Example:
// VerifyAll(t, "uppercase", []string("dog", "cat"}, func(x interface{}) string { return strings.ToUpper(x.(string)) })
func VerifyAll(t Failable, header string, collection interface{}, transform func(interface{}) string) error {
if len(header) != 0 {
header = fmt.Sprintf("%s\n\n\n", header)
}
outputText := header + strings.Join(utils.MapToString(collection, transform), "\n")
return VerifyString(t, outputText)
}
type reporterCloser struct {
reporter *reporters.Reporter
}
func (s *reporterCloser) Close() error {
defaultReporter = s.reporter
return nil
}
type frontLoadedReporterCloser struct {
reporter *reporters.Reporter
}
func (s *frontLoadedReporterCloser) Close() error {
defaultFrontLoadedReporter = s.reporter
return nil
}
// UseReporter configures which reporter to use on failure.
// Add at the test or method level to configure your reporter.
//
// The following examples shows how to use a reporter for all of your test cases
// in a package directory through go's setup feature.
//
//
// func TestMain(m *testing.M) {
// r := UseReporter(reporters.NewBeyondCompareReporter())
// defer r.Close()
//
// os.Exit(m.Run())
// }
//
func UseReporter(reporter reporters.Reporter) io.Closer {
closer := &reporterCloser{
reporter: defaultReporter,
}
defaultReporter = &reporter
return closer
}
// UseFrontLoadedReporter configures reporters ahead of all other reporters to
// handle situations like CI. These reporters usually prevent reporting in
// scenarios that are headless.
func UseFrontLoadedReporter(reporter reporters.Reporter) io.Closer {
closer := &frontLoadedReporterCloser{
reporter: defaultFrontLoadedReporter,
}
defaultFrontLoadedReporter = &reporter
return closer
}
func getReporter() reporters.Reporter {
return reporters.NewFirstWorkingReporter(
*defaultFrontLoadedReporter,
*defaultReporter,
)
}
|