88 "io"
99 "os"
1010 "reflect"
11+ "regexp"
1112 "strings"
1213
1314 "github.com/approvals/go-approval-tests/reporters"
@@ -32,13 +33,49 @@ type Failable interface {
3233}
3334
3435// VerifyWithExtension Example:
35- // VerifyWithExtension(t, strings.NewReader("Hello"), ".txt")
36- func VerifyWithExtension (t Failable , reader io.Reader , extWithDot string ) {
36+ // VerifyWithExtension(t, strings.NewReader("Hello"), ".json")
37+ // Deprecated: Please use Verify with the Options() fluent syntax.
38+ func VerifyWithExtension (t Failable , reader io.Reader , extWithDot string , opts ... verifyOptions ) {
3739 t .Helper ()
40+ Verify (t , reader , alwaysOption (opts ).WithExtension (extWithDot ))
41+ }
42+
43+ // Verify Example:
44+ // Verify(t, strings.NewReader("Hello"))
45+ func Verify (t Failable , reader io.Reader , opts ... verifyOptions ) {
46+ t .Helper ()
47+
48+ if len (opts ) > 1 {
49+ panic ("Please use fluent syntax for options, see documentation for more information" )
50+ }
51+
52+ var extWithDot string
53+ if len (opts ) == 0 || opts [0 ].extWithDot == "" {
54+ extWithDot = ".txt"
55+ } else {
56+ extWithDot = opts [0 ].extWithDot
57+ }
58+
3859 namer := getApprovalName (t )
3960
61+ if len (opts ) > 0 {
62+ b , err := io .ReadAll (reader )
63+ if err != nil {
64+ panic (err )
65+ }
66+
67+ result := string (b )
68+ for _ , o := range opts {
69+ for _ , sb := range o .scrubbers {
70+ result = sb (result )
71+ }
72+ }
73+
74+ reader = strings .NewReader (result )
75+ }
76+
4077 reporter := getReporter ()
41- var err = namer .compare (namer .getApprovalFile (extWithDot ), namer .getReceivedFile (extWithDot ), reader )
78+ err : = namer .compare (namer .getApprovalFile (extWithDot ), namer .getReceivedFile (extWithDot ), reader )
4279 if err != nil {
4380 reporter .Report (namer .getApprovalFile (extWithDot ), namer .getReceivedFile (extWithDot ))
4481 t .Log ("Failed Approval: received does not match approved." )
@@ -48,24 +85,17 @@ func VerifyWithExtension(t Failable, reader io.Reader, extWithDot string) {
4885 }
4986}
5087
51- // Verify Example:
52- // Verify(t, strings.NewReader("Hello"))
53- func Verify (t Failable , reader io.Reader ) {
54- t .Helper ()
55- VerifyWithExtension (t , reader , ".txt" )
56- }
57-
5888// VerifyString stores the passed string into the received file and confirms
5989// that it matches the approved local file. On failure, it will launch a reporter.
60- func VerifyString (t Failable , s string ) {
90+ func VerifyString (t Failable , s string , opts ... verifyOptions ) {
6191 t .Helper ()
6292 reader := strings .NewReader (s )
63- Verify (t , reader )
93+ Verify (t , reader , opts ... )
6494}
6595
6696// VerifyXMLStruct Example:
6797// VerifyXMLStruct(t, xml)
68- func VerifyXMLStruct (t Failable , obj interface {}) {
98+ func VerifyXMLStruct (t Failable , obj interface {}, opts ... verifyOptions ) {
6999 t .Helper ()
70100 xmlContent , err := xml .MarshalIndent (obj , "" , " " )
71101 if err != nil {
@@ -74,15 +104,15 @@ func VerifyXMLStruct(t Failable, obj interface{}) {
74104 tip = "when using anonymous types be sure to include\n XMLName xml.Name `xml:\" Your_Name_Here\" `\n "
75105 }
76106 message := fmt .Sprintf ("error while pretty printing XML\n %verror:\n %v\n XML:\n %v\n " , tip , err , obj )
77- VerifyWithExtension (t , strings .NewReader (message ), ".xml" )
107+ Verify (t , strings .NewReader (message ), alwaysOption ( opts ). WithExtension ( ".xml" ) )
78108 } else {
79- VerifyWithExtension (t , bytes .NewReader (xmlContent ), ".xml" )
109+ Verify (t , bytes .NewReader (xmlContent ), alwaysOption ( opts ). WithExtension ( ".xml" ) )
80110 }
81111}
82112
83113// VerifyXMLBytes Example:
84114// VerifyXMLBytes(t, []byte("<Test/>"))
85- func VerifyXMLBytes (t Failable , bs []byte ) {
115+ func VerifyXMLBytes (t Failable , bs []byte , opts ... verifyOptions ) {
86116 t .Helper ()
87117 type node struct {
88118 Attr []xml.Attr
@@ -95,65 +125,66 @@ func VerifyXMLBytes(t Failable, bs []byte) {
95125 err := xml .Unmarshal (bs , & x )
96126 if err != nil {
97127 message := fmt .Sprintf ("error while parsing XML\n error:\n %s\n XML:\n %s\n " , err , string (bs ))
98- VerifyWithExtension (t , strings .NewReader (message ), ".xml" )
128+ Verify (t , strings .NewReader (message ), alwaysOption ( opts ). WithExtension ( ".xml" ) )
99129 } else {
100- VerifyXMLStruct (t , x )
130+ VerifyXMLStruct (t , x , opts ... )
101131 }
102132}
103133
104134// VerifyJSONStruct Example:
105135// VerifyJSONStruct(t, json)
106- func VerifyJSONStruct (t Failable , obj interface {}) {
136+ func VerifyJSONStruct (t Failable , obj interface {}, opts ... verifyOptions ) {
107137 t .Helper ()
138+
108139 jsonb , err := json .MarshalIndent (obj , "" , " " )
109140 if err != nil {
110141 message := fmt .Sprintf ("error while pretty printing JSON\n error:\n %s\n JSON:\n %s\n " , err , obj )
111- VerifyWithExtension (t , strings .NewReader (message ), ".json" )
142+ Verify (t , strings .NewReader (message ), alwaysOption ( opts ). WithExtension ( ".json" ) )
112143 } else {
113- VerifyWithExtension (t , bytes .NewReader (jsonb ), ".json" )
144+ Verify (t , bytes .NewReader (jsonb ), alwaysOption ( opts ). WithExtension ( ".json" ) )
114145 }
115146}
116147
117148// VerifyJSONBytes Example:
118149// VerifyJSONBytes(t, []byte("{ \"Greeting\": \"Hello\" }"))
119- func VerifyJSONBytes (t Failable , bs []byte ) {
150+ func VerifyJSONBytes (t Failable , bs []byte , opts ... verifyOptions ) {
120151 t .Helper ()
121152 var obj map [string ]interface {}
122153 err := json .Unmarshal (bs , & obj )
123154 if err != nil {
124155 message := fmt .Sprintf ("error while parsing JSON\n error:\n %s\n JSON:\n %s\n " , err , string (bs ))
125- VerifyWithExtension (t , strings .NewReader (message ), ".json" )
156+ Verify (t , strings .NewReader (message ), alwaysOption ( opts ). WithExtension ( ".json" ) )
126157 } else {
127- VerifyJSONStruct (t , obj )
158+ VerifyJSONStruct (t , obj , opts ... )
128159 }
129160}
130161
131162// VerifyMap Example:
132163// VerifyMap(t, map[string][string] { "dog": "bark" })
133- func VerifyMap (t Failable , m interface {}) {
164+ func VerifyMap (t Failable , m interface {}, opts ... verifyOptions ) {
134165 t .Helper ()
135166 outputText := utils .PrintMap (m )
136- VerifyString (t , outputText )
167+ VerifyString (t , outputText , opts ... )
137168}
138169
139170// VerifyArray Example:
140171// VerifyArray(t, []string{"dog", "cat"})
141- func VerifyArray (t Failable , array interface {}) {
172+ func VerifyArray (t Failable , array interface {}, opts ... verifyOptions ) {
142173 t .Helper ()
143174 outputText := utils .PrintArray (array )
144- VerifyString (t , outputText )
175+ VerifyString (t , outputText , opts ... )
145176}
146177
147178// VerifyAll Example:
148179// VerifyAll(t, "uppercase", []string("dog", "cat"}, func(x interface{}) string { return strings.ToUpper(x.(string)) })
149- func VerifyAll (t Failable , header string , collection interface {}, transform func (interface {}) string ) {
180+ func VerifyAll (t Failable , header string , collection interface {}, transform func (interface {}) string , opts ... verifyOptions ) {
150181 t .Helper ()
151182 if len (header ) != 0 {
152183 header = fmt .Sprintf ("%s\n \n \n " , header )
153184 }
154185
155186 outputText := header + strings .Join (utils .MapToString (collection , transform ), "\n " )
156- VerifyString (t , outputText )
187+ VerifyString (t , outputText , opts ... )
157188}
158189
159190type reporterCloser struct {
@@ -232,3 +263,42 @@ func getReporter() reporters.Reporter {
232263func UseFolder (f string ) {
233264 defaultFolder = f
234265}
266+
267+ type scrubber func (s string ) string
268+
269+ // verifyOptions can be accessed via the approvals.Options() API enabling configuration of scrubbers
270+ type verifyOptions struct {
271+ scrubbers []scrubber
272+ extWithDot string
273+ }
274+
275+ // Options enables providing individual Verify functions with customisations such as scrubbers
276+ func Options () verifyOptions {
277+ return verifyOptions {}
278+ }
279+
280+ // WithRegexScrubber allows you to 'scrub' dynamic data such as timestamps within your test input
281+ // and replace it with a static placeholder
282+ func (v verifyOptions ) WithRegexScrubber (scrubber * regexp.Regexp , replacer string ) verifyOptions {
283+ v .scrubbers = append (v .scrubbers , func (s string ) string {
284+ return scrubber .ReplaceAllString (s , replacer )
285+ })
286+ return v
287+ }
288+
289+ // WithExtension overrides the default file extension (.txt) for approval files.
290+ func (v verifyOptions ) WithExtension (extension string ) verifyOptions {
291+ v .extWithDot = extension
292+ return v
293+ }
294+
295+ func alwaysOption (opts []verifyOptions ) verifyOptions {
296+ var v verifyOptions
297+ if len (opts ) == 0 {
298+ v = Options ()
299+ } else {
300+ v = opts [0 ]
301+ }
302+
303+ return v
304+ }
0 commit comments