Skip to content

Commit f277694

Browse files
committed
feat: save partial progress when test fails
1 parent 92ce944 commit f277694

File tree

2 files changed

+57
-19
lines changed

2 files changed

+57
-19
lines changed

gocksnap.go

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ type Call struct {
5858

5959
// Snapshot holds the state of the snapshot being recorded, which can include multiple HTTP calls.
6060
type Snapshot struct {
61-
Calls []Call `json:"calls"`
61+
Partial bool `json:"partial,omitempty"`
62+
Calls []Call `json:"calls"`
6263

6364
// testName used to identify the snapshot file.
6465
testName string
@@ -79,19 +80,18 @@ type Snapshot struct {
7980
sseConns map[chan string]struct{}
8081
}
8182

82-
// Finish
83-
func (g *Snapshot) Finish(t *testing.T) {
84-
t.Helper()
85-
86-
if !g.updateMode {
87-
if !gock.IsDone() {
88-
t.Fatalf("Snapshot '%s' is not complete. Some requests were not mocked.", g.name)
83+
func (g *Snapshot) sendMessage(msg string) {
84+
for ch := range g.sseConns {
85+
select {
86+
case ch <- msg:
87+
default:
8988
}
90-
91-
return
9289
}
90+
}
91+
92+
func (g *Snapshot) saveToFile(t *testing.T) {
93+
t.Helper()
9394

94-
// Update snapshot
9595
data, err := json.MarshalIndent(g, "", " ")
9696
if err != nil {
9797
t.Fatalf("Failed to marshal snapshot '%s': %v", g.name, err)
@@ -105,6 +105,35 @@ func (g *Snapshot) Finish(t *testing.T) {
105105
}
106106
}
107107

108+
func (g *Snapshot) attemptToSavePartial(t *testing.T) {
109+
t.Helper()
110+
111+
if !t.Failed() || !g.updateMode || len(g.Calls) == 0 {
112+
return
113+
}
114+
115+
g.sendMessage("failedPartial")
116+
117+
g.Partial = true
118+
g.saveToFile(t)
119+
}
120+
121+
// Finish
122+
func (g *Snapshot) Finish(t *testing.T) {
123+
t.Helper()
124+
125+
if !g.updateMode {
126+
if !gock.IsDone() {
127+
t.Fatalf("Snapshot '%s' is not complete. Some requests were not mocked.", g.name)
128+
}
129+
130+
return
131+
}
132+
133+
// Update snapshot
134+
g.saveToFile(t)
135+
}
136+
108137
// file returns the path to the snapshot file.
109138
func (g *Snapshot) file() string {
110139
return filepath.Join(defaultSnapshotDirectory, strings.ReplaceAll(strings.ReplaceAll(g.testName+"-"+g.name, " ", "_"), "/", "_")+".json")
@@ -136,20 +165,15 @@ func (g *Snapshot) promptCall(req *http.Request, existingCall *Call) *Call {
136165
Headers: req.Header,
137166
QueryParams: queryParams,
138167
},
139-
finalCall: finalCall,
168+
finalCall: finalCall,
140169
}
141170

142171
if existingCall != nil {
143172
g.pending.ExistingResponse = &existingCall.MockedCall
144173
}
145174

146175
// notify SSE clients
147-
for ch := range g.sseConns {
148-
select {
149-
case ch <- "pending":
150-
default:
151-
}
152-
}
176+
g.sendMessage("pending")
153177

154178
g.mu.Unlock()
155179

@@ -190,6 +214,11 @@ func MatchSnapshot(t *testing.T, snapshotName string) *Snapshot {
190214
t.Fatalf("Failed to unmarshal snapshot '%s': %v", snapshot.file(), err)
191215
}
192216

217+
if snapshot.Partial {
218+
snapshot.updateMode = true
219+
snapshot.Partial = false // reset partial flag for the new run
220+
}
221+
193222
if snapshot.updateMode {
194223
existingCalls = snapshot.Calls
195224
snapshot.Calls = make([]Call, 0)
@@ -209,6 +238,8 @@ func MatchSnapshot(t *testing.T, snapshotName string) *Snapshot {
209238
gock.Intercept()
210239

211240
if snapshot.updateMode {
241+
t.Cleanup(func() { snapshot.attemptToSavePartial(t) })
242+
212243
var existingCall *Call
213244
if len(existingCalls) > 0 {
214245
existingCall = &existingCalls[0]

index.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,14 @@ <h1 id="title">Gocksnap Console</h1>
144144

145145
<script>
146146
(function () {
147+
let isFailed = false;
147148
const eventSource = new EventSource('/events');
148149
eventSource.onmessage = function (evt) {
149150
if (evt.data === 'pending') fetchCurrent();
151+
if (evt.data === 'failedPartial') {
152+
isFailed = true;
153+
document.getElementById('content').innerHTML = '<p id="msg" style="color: red;">⚠️ The test has failed, saving the partial snapshot</p>';
154+
}
150155
};
151156
fetchCurrent();
152157

@@ -201,7 +206,6 @@ <h1 id="title">Gocksnap Console</h1>
201206

202207
function showForm(data) {
203208
document.getElementById('title').textContent = "Snapshot for: " + data.name;
204-
console.log('existingResponse', data.existingResponse);
205209
let optionalFields = '';
206210
if (data.request.headers) {
207211
optionalFields += createOptionalParamsDiv('headers', 'Headers', Object.entries(data.request.headers).map(([key, value]) => ({
@@ -295,6 +299,9 @@ <h3>Existing Snapshot</h3>
295299
matchingQueryParams: getOptionalParams('queryParams'),
296300
}),
297301
}).then(function () {
302+
if (isFailed) {
303+
return;
304+
}
298305
document.getElementById('content').innerHTML = '<p>✅ Using existing snapshot. Waiting for the next one…</p>';
299306
});
300307
};

0 commit comments

Comments
 (0)