Skip to content

Commit f81115d

Browse files
authored
feat: implement matchYAML (#114)
1 parent 09cd270 commit f81115d

File tree

18 files changed

+1051
-127
lines changed

18 files changed

+1051
-127
lines changed

README.md

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Go Reference](https://pkg.go.dev/badge/github.com/gkampitakis/go-snaps.svg)](https://pkg.go.dev/github.com/gkampitakis/go-snaps)
66

77
<p align="center">
8-
<b>Jest-like snapshot testing in Golang</b>
8+
<b>Jest-like snapshot testing in Go</b>
99
</p>
1010

1111
<br>
@@ -18,12 +18,13 @@
1818

1919
- [Installation](#installation)
2020
- [MatchSnapshot](#matchsnapshot)
21+
- [MatchStandaloneSnapshot](#matchstandalonesnapshot) `New`
2122
- [MatchJSON](#matchjson)
22-
- [Matchers](#matchers)
23-
- [match.Any](#matchany)
24-
- [match.Custom](#matchcustom)
25-
- [match.Type\[ExpectedType\]](#matchtype)
26-
- [MatchStandaloneSnapshot](#matchstandalonesnapshot)
23+
- [MatchYAML](#matchyaml) `New`
24+
- [Matchers](#matchers)
25+
- [match.Any](#matchany)
26+
- [match.Custom](#matchcustom)
27+
- [match.Type\[ExpectedType\]](#matchtype)
2728
- [Configuration](#configuration)
2829
- [Update Snapshots](#update-snapshots)
2930
- [Clean obsolete Snapshots](#clean-obsolete-snapshots)
@@ -85,6 +86,29 @@ name is the test file name with extension `.snap`.
8586
So for example if your test is called `test_simple.go` when you run your tests, a snapshot file
8687
will be created at `./__snapshots__/test_simple.snaps`.
8788

89+
## MatchStandaloneSnapshot
90+
91+
`MatchStandaloneSnapshot` will create snapshots on separate files as opposed to `MatchSnapshot` which adds multiple snapshots inside the same file.
92+
93+
_Combined with `snaps.Ext` you can have proper syntax highlighting and better readability_
94+
95+
```go
96+
// test_simple.go
97+
98+
func TestSimple(t *testing.T) {
99+
snaps.MatchStandaloneSnapshot(t, "Hello World")
100+
// or create an html snapshot file
101+
snaps.WithConfig(snaps.Ext(".html")).
102+
MatchStandaloneSnapshot(t, "<html><body><h1>Hello World</h1></body></html>")
103+
}
104+
```
105+
106+
`go-snaps` saves the snapshots in `__snapshots__` directory and the file
107+
name is the test file name with extension `.snap`.
108+
109+
So for example if your test is called `test_simple.go` when you run your tests, a snapshot file
110+
will be created at `./__snapshots__/TestSimple_1.snaps`.
111+
88112
## MatchJSON
89113

90114
`MatchJSON` can be used to capture data that can represent a valid json.
@@ -107,21 +131,51 @@ func TestJSON(t *testing.T) {
107131

108132
JSON will be saved in snapshot in pretty format for more readability and deterministic diffs.
109133

134+
## MatchYAML
135+
136+
`MatchYAML` can be used to capture data that can represent a valid yaml.
137+
138+
You can pass a valid json in form of `string` or `[]byte` or whatever value can be passed
139+
successfully on `yaml.Marshal`.
140+
141+
```go
142+
func TestYAML(t *testing.T) {
143+
type User struct {
144+
Age int
145+
Email string
146+
}
147+
148+
snaps.MatchYAML(t, "user: \"mock-user\"\nage: 10\nemail: [email protected]")
149+
snaps.MatchYAML(t, []byte("user: \"mock-user\"\nage: 10\nemail: [email protected]"))
150+
snaps.MatchYAML(t, User{10, "mock-email"})
151+
}
152+
```
153+
110154
### Matchers
111155

112-
`MatchJSON`'s third argument can accept a list of matchers. Matchers are functions that can act
156+
`MatchJSON`'s and `MatchYAML`'s third argument can accept a list of matchers. Matchers are functions that can act
113157
as property matchers and test values.
114158

115159
You can pass the path of the property you want to match and test.
116160

117-
_More information about the supported path syntax from [gjson](https://github.com/tidwall/gjson/blob/v1.17.0/SYNTAX.md)._
118-
119161
Currently `go-snaps` has three build in matchers
120162

121163
- `match.Any`
122164
- `match.Custom`
123165
- `match.Type[ExpectedType]`
124166

167+
_Open to feedback for building more matchers or you can build your own [example](./examples/matchJSON_test.go#L16)._
168+
169+
#### Path Syntax
170+
171+
For JSON go-snaps utilises gjson.
172+
173+
_More information about the supported path syntax from [gjson](https://github.com/tidwall/gjson/blob/v1.17.0/SYNTAX.md)._
174+
175+
As for YAML go-snaps utilises [github.com/goccy/go-yaml#5-use-yamlpath](https://github.com/goccy/go-yaml#5-use-yamlpath).
176+
177+
_More information about the supported syntax [PathString](https://github.com/goccy/go-yaml/blob/9cbf5d4217830fd4ad1504e9ed117c183ade0994/path.go#L17-L26)._
178+
125179
#### match.Any
126180

127181
Any matcher acts as a placeholder for any value. It replaces any targeted path with a
@@ -196,29 +250,6 @@ match.Type[string]("user.info").
196250

197251
You can see more [examples](./examples/matchJSON_test.go#L96).
198252

199-
## MatchStandaloneSnapshot
200-
201-
`MatchStandaloneSnapshot` will create snapshots on separate files as opposed to `MatchSnapshot` which adds multiple snapshots inside the same file.
202-
203-
_Combined with `snaps.Ext` you can have proper syntax highlighting and better readability_
204-
205-
```go
206-
// test_simple.go
207-
208-
func TestSimple(t *testing.T) {
209-
snaps.MatchStandaloneSnapshot(t, "Hello World")
210-
// or create an html snapshot file
211-
snaps.WithConfig(snaps.Ext(".html")).
212-
MatchStandaloneSnapshot(t, "<html><body><h1>Hello World</h1></body></html>")
213-
}
214-
```
215-
216-
`go-snaps` saves the snapshots in `__snapshots__` directory and the file
217-
name is the test file name with extension `.snap`.
218-
219-
So for example if your test is called `test_simple.go` when you run your tests, a snapshot file
220-
will be created at `./__snapshots__/TestSimple_1.snaps`.
221-
222253
## Configuration
223254

224255
`go-snaps` allows passing configuration for overriding
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
[TestMatchYaml/should_match_struct_yaml - 1]
3+
name: John Doe
4+
age: 30
5+
email: john.doe@example.com
6+
address: mock-address
7+
time: mock-time
8+
9+
---
10+
11+
[TestMatchYaml/custom_matching_logic - 1]
12+
name: mock-user
13+
email: mock-user@email.com
14+
keys:
15+
- 1
16+
- 2
17+
- 3
18+
- 4
19+
- 5
20+
21+
---
22+
23+
[TestMatchYaml/type_matcher - 1]
24+
data: <Type:uint64>
25+
---
26+
27+
[TestMatchYaml/type_matcher - 2]
28+
metadata: <Type:map[string]interface {}>
29+
---

examples/matchYAML_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package examples
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
"time"
7+
8+
"github.com/gkampitakis/go-snaps/match"
9+
"github.com/gkampitakis/go-snaps/snaps"
10+
)
11+
12+
func TestMatchYaml(t *testing.T) {
13+
t.Run("should match struct yaml", func(t *testing.T) {
14+
type User struct {
15+
Name string `yaml:"name"`
16+
Age int `yaml:"age"`
17+
Email string `yaml:"email"`
18+
Address string `yaml:"address"`
19+
Time time.Time `yaml:"time"`
20+
}
21+
22+
snaps.MatchYAML(t, User{
23+
Name: "John Doe",
24+
Age: 30,
25+
26+
Address: "123 Main St",
27+
Time: time.Now(),
28+
}, match.Any("$.time").Placeholder("mock-time"), match.Any("$.address").Placeholder("mock-address"))
29+
})
30+
31+
t.Run("custom matching logic", func(t *testing.T) {
32+
type User struct {
33+
Name string `json:"name"`
34+
Email string `json:"email"`
35+
Keys []int `json:"keys"`
36+
}
37+
38+
u := User{
39+
Name: "mock-user",
40+
41+
Keys: []int{1, 2, 3, 4, 5},
42+
}
43+
44+
snaps.MatchYAML(t, u, match.Custom("$.keys", func(val any) (any, error) {
45+
keys, ok := val.([]any)
46+
if !ok {
47+
return nil, fmt.Errorf("expected []any but got %T", val)
48+
}
49+
50+
if len(keys) > 5 {
51+
return nil, fmt.Errorf("expected less than 5 keys")
52+
}
53+
54+
return val, nil
55+
}))
56+
})
57+
58+
t.Run("type matcher", func(t *testing.T) {
59+
snaps.MatchYAML(t, "data: 10", match.Type[uint64]("$.data"))
60+
61+
snaps.MatchYAML(
62+
t,
63+
"metadata:\n timestamp: 1687108093142",
64+
match.Type[map[string]any]("$.metadata"),
65+
)
66+
})
67+
}

go.mod

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
module github.com/gkampitakis/go-snaps
22

3-
go 1.21
3+
go 1.22
44

55
require (
6-
github.com/gkampitakis/ciinfo v0.3.0
6+
github.com/gkampitakis/ciinfo v0.3.1
77
github.com/gkampitakis/go-diff v1.3.2
8+
github.com/goccy/go-yaml v1.15.13
89
github.com/kr/pretty v0.3.1
910
github.com/maruel/natural v1.1.1
10-
github.com/tidwall/gjson v1.17.0
11+
github.com/tidwall/gjson v1.18.0
1112
github.com/tidwall/pretty v1.2.1
1213
github.com/tidwall/sjson v1.2.5
1314
)
1415

1516
require (
1617
github.com/kr/text v0.2.0 // indirect
17-
github.com/rogpeppe/go-internal v1.12.0 // indirect
18+
github.com/rogpeppe/go-internal v1.13.1 // indirect
1819
github.com/tidwall/match v1.1.1 // indirect
1920
)

go.sum

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
2-
github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
3-
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
2+
github.com/gkampitakis/ciinfo v0.3.1 h1:lzjbemlGI4Q+XimPg64ss89x8Mf3xihJqy/0Mgagapo=
3+
github.com/gkampitakis/ciinfo v0.3.1/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
44
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
55
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
6+
github.com/goccy/go-yaml v1.15.13 h1:Xd87Yddmr2rC1SLLTm2MNDcTjeO/GYo0JGiww6gSTDg=
7+
github.com/goccy/go-yaml v1.15.13/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
68
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
79
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
810
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -11,11 +13,11 @@ github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
1113
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
1214
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
1315
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
14-
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
15-
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
16+
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
17+
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
1618
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
17-
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
18-
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
19+
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
20+
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
1921
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
2022
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
2123
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=

0 commit comments

Comments
 (0)