1
+ import { server } from './mock'
2
+ import { API } from '../src'
3
+ import { http , HttpResponse } from 'msw'
4
+
5
+ let client : API
6
+
7
+ beforeAll ( ( ) => {
8
+ client = new API ( process . env . HACKMD_ACCESS_TOKEN ! )
9
+ return server . listen ( )
10
+ } )
11
+
12
+ afterEach ( ( ) => {
13
+ server . resetHandlers ( )
14
+ } )
15
+
16
+ afterAll ( ( ) => {
17
+ server . close ( )
18
+ // Add explicit cleanup to ensure Jest exits properly
19
+ return new Promise ( resolve => setTimeout ( resolve , 100 ) )
20
+ } )
21
+
22
+ describe ( 'Etag support' , ( ) => {
23
+ // Helper to reset server between tests
24
+ beforeEach ( ( ) => {
25
+ server . resetHandlers ( )
26
+ } )
27
+
28
+ test ( 'response includes etag when server provides it (unwrapData: true)' , async ( ) => {
29
+ // Setup mock server to return an etag
30
+ const mockEtag = 'W/"123456789"'
31
+
32
+ server . use (
33
+ http . get ( 'https://api.hackmd.io/v1/notes/test-note-id' , ( ) => {
34
+ return HttpResponse . json (
35
+ {
36
+ id : 'test-note-id' ,
37
+ title : 'Test Note'
38
+ } ,
39
+ {
40
+ headers : {
41
+ 'ETag' : mockEtag
42
+ }
43
+ }
44
+ )
45
+ } )
46
+ )
47
+
48
+ // Make request with default unwrapData: true
49
+ const response = await client . getNote ( 'test-note-id' )
50
+
51
+ // Verify response has etag property
52
+ expect ( response ) . toHaveProperty ( 'etag' , mockEtag )
53
+
54
+ // Verify data properties still exist
55
+ expect ( response ) . toHaveProperty ( 'id' , 'test-note-id' )
56
+ expect ( response ) . toHaveProperty ( 'title' , 'Test Note' )
57
+ } )
58
+
59
+ test ( 'response includes etag in headers when unwrapData is false' , async ( ) => {
60
+ // Setup mock server to return an etag
61
+ const mockEtag = 'W/"123456789"'
62
+
63
+ server . use (
64
+ http . get ( 'https://api.hackmd.io/v1/notes/test-note-id' , ( ) => {
65
+ return HttpResponse . json (
66
+ {
67
+ id : 'test-note-id' ,
68
+ title : 'Test Note'
69
+ } ,
70
+ {
71
+ headers : {
72
+ 'ETag' : mockEtag
73
+ }
74
+ }
75
+ )
76
+ } )
77
+ )
78
+
79
+ // Make request with unwrapData: false
80
+ const response = await client . getNote ( 'test-note-id' , { unwrapData : false } )
81
+
82
+ // Verify response headers contain etag
83
+ expect ( response . headers . etag ) . toBe ( mockEtag )
84
+
85
+ // Verify data is in response.data
86
+ expect ( response . data ) . toHaveProperty ( 'id' , 'test-note-id' )
87
+ expect ( response . data ) . toHaveProperty ( 'title' , 'Test Note' )
88
+ } )
89
+
90
+ test ( 'sends If-None-Match header when etag is provided' , async ( ) => {
91
+ // Setup mock server to check for If-None-Match header
92
+ let ifNoneMatchValue : string | null = null
93
+ const mockEtag = 'W/"123456789"'
94
+
95
+ server . use (
96
+ http . get ( 'https://api.hackmd.io/v1/notes/test-note-id' , ( { request } ) => {
97
+ // Store the If-None-Match header value for verification
98
+ ifNoneMatchValue = request . headers . get ( 'If-None-Match' )
99
+
100
+ return HttpResponse . json (
101
+ {
102
+ id : 'test-note-id' ,
103
+ title : 'Test Note'
104
+ } ,
105
+ {
106
+ headers : {
107
+ 'ETag' : mockEtag
108
+ }
109
+ }
110
+ )
111
+ } )
112
+ )
113
+
114
+ // Make request with etag in options
115
+ await client . getNote ( 'test-note-id' , { etag : mockEtag } )
116
+
117
+ // Verify the If-None-Match header was sent with correct value
118
+ expect ( ifNoneMatchValue ) . toBe ( mockEtag )
119
+ } )
120
+
121
+ test ( 'handles 304 Not Modified responses correctly (unwrapData: false)' , async ( ) => {
122
+ // Setup mock server to return 304 when etag matches
123
+ const mockEtag = 'W/"123456789"'
124
+
125
+ server . use (
126
+ http . get ( 'https://api.hackmd.io/v1/notes/test-note-id' , ( { request } ) => {
127
+ const ifNoneMatch = request . headers . get ( 'If-None-Match' )
128
+
129
+ // Return 304 when etag matches
130
+ if ( ifNoneMatch === mockEtag ) {
131
+ return new HttpResponse ( null , {
132
+ status : 304 ,
133
+ headers : {
134
+ 'ETag' : mockEtag
135
+ }
136
+ } )
137
+ }
138
+
139
+ return HttpResponse . json (
140
+ {
141
+ id : 'test-note-id' ,
142
+ title : 'Test Note'
143
+ } ,
144
+ {
145
+ headers : {
146
+ 'ETag' : mockEtag
147
+ }
148
+ }
149
+ )
150
+ } )
151
+ )
152
+
153
+ // Request with unwrapData: false to get full response including status
154
+ const response = await client . getNote ( 'test-note-id' , { etag : mockEtag , unwrapData : false } )
155
+
156
+ // Verify we get a 304 status code
157
+ expect ( response . status ) . toBe ( 304 )
158
+
159
+ // Verify etag is still available in headers
160
+ expect ( response . headers . etag ) . toBe ( mockEtag )
161
+ } )
162
+
163
+ test ( 'handles 304 Not Modified responses correctly (unwrapData: true)' , async ( ) => {
164
+ // Setup mock server to return 304 when etag matches
165
+ const mockEtag = 'W/"123456789"'
166
+
167
+ server . use (
168
+ http . get ( 'https://api.hackmd.io/v1/notes/test-note-id' , ( { request } ) => {
169
+ const ifNoneMatch = request . headers . get ( 'If-None-Match' )
170
+
171
+ // Return 304 when etag matches
172
+ if ( ifNoneMatch === mockEtag ) {
173
+ return new HttpResponse ( null , {
174
+ status : 304 ,
175
+ headers : {
176
+ 'ETag' : mockEtag
177
+ }
178
+ } )
179
+ }
180
+
181
+ return HttpResponse . json (
182
+ {
183
+ id : 'test-note-id' ,
184
+ title : 'Test Note'
185
+ } ,
186
+ {
187
+ headers : {
188
+ 'ETag' : mockEtag
189
+ }
190
+ }
191
+ )
192
+ } )
193
+ )
194
+
195
+ // Request with default unwrapData: true
196
+ const response = await client . getNote ( 'test-note-id' , { etag : mockEtag } )
197
+
198
+ // With unwrapData: true and a 304 response, we just get the etag
199
+ expect ( response ) . toHaveProperty ( 'etag' , mockEtag )
200
+ expect ( response ) . toHaveProperty ( 'status' , 304 )
201
+ } )
202
+ } )
0 commit comments