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