@@ -1170,6 +1170,201 @@ describe('RequestInterceptor', () => {
11701170 } ) ;
11711171 } ) ;
11721172
1173+ describe ( 'Content-Type validation' , ( ) => {
1174+ it ( 'should not validate Content-Type by default' , async ( ) => {
1175+ mockFetch . mockResolvedValueOnce (
1176+ new Response ( 'hello' , {
1177+ status : 200 ,
1178+ statusText : 'OK' ,
1179+ headers : { 'Content-Type' : 'text/html' } ,
1180+ } )
1181+ ) ;
1182+
1183+ const api = RequestInterceptor . create ( {
1184+ baseUrl : 'https://api.example.com' ,
1185+ } ) ;
1186+
1187+ const response = await api . fetch ( '/page' ) ;
1188+ expect ( response . status ) . toBe ( 200 ) ;
1189+
1190+ api . destroy ( ) ;
1191+ } ) ;
1192+
1193+ it ( 'should pass when response Content-Type matches expected type' , async ( ) => {
1194+ mockFetch . mockResolvedValueOnce (
1195+ new Response ( '{"ok":true}' , {
1196+ status : 200 ,
1197+ statusText : 'OK' ,
1198+ headers : { 'Content-Type' : 'application/json' } ,
1199+ } )
1200+ ) ;
1201+
1202+ const api = RequestInterceptor . create ( {
1203+ baseUrl : 'https://api.example.com' ,
1204+ expectedContentType : 'application/json' ,
1205+ } ) ;
1206+
1207+ const response = await api . fetch ( '/data' ) ;
1208+ expect ( response . status ) . toBe ( 200 ) ;
1209+
1210+ api . destroy ( ) ;
1211+ } ) ;
1212+
1213+ it ( 'should pass when Content-Type has parameters' , async ( ) => {
1214+ mockFetch . mockResolvedValueOnce (
1215+ new Response ( '{"ok":true}' , {
1216+ status : 200 ,
1217+ statusText : 'OK' ,
1218+ headers : { 'Content-Type' : 'application/json; charset=utf-8' } ,
1219+ } )
1220+ ) ;
1221+
1222+ const api = RequestInterceptor . create ( {
1223+ baseUrl : 'https://api.example.com' ,
1224+ expectedContentType : 'application/json' ,
1225+ } ) ;
1226+
1227+ const response = await api . fetch ( '/data' ) ;
1228+ expect ( response . status ) . toBe ( 200 ) ;
1229+
1230+ api . destroy ( ) ;
1231+ } ) ;
1232+
1233+ it ( 'should throw CONTENT_TYPE_MISMATCH on mismatch' , async ( ) => {
1234+ mockFetch . mockResolvedValueOnce (
1235+ new Response ( '<html></html>' , {
1236+ status : 200 ,
1237+ statusText : 'OK' ,
1238+ headers : { 'Content-Type' : 'text/html' } ,
1239+ } )
1240+ ) ;
1241+
1242+ const api = RequestInterceptor . create ( {
1243+ baseUrl : 'https://api.example.com' ,
1244+ expectedContentType : 'application/json' ,
1245+ } ) ;
1246+
1247+ await expect ( api . fetch ( '/page' ) ) . rejects . toThrow ( 'Content-Type mismatch' ) ;
1248+
1249+ api . destroy ( ) ;
1250+ } ) ;
1251+
1252+ it ( 'should throw when response has no Content-Type (fail closed)' , async ( ) => {
1253+ mockFetch . mockResolvedValueOnce (
1254+ new Response ( 'data' , {
1255+ status : 200 ,
1256+ statusText : 'OK' ,
1257+ } )
1258+ ) ;
1259+
1260+ const api = RequestInterceptor . create ( {
1261+ baseUrl : 'https://api.example.com' ,
1262+ expectedContentType : 'application/json' ,
1263+ } ) ;
1264+
1265+ await expect ( api . fetch ( '/data' ) ) . rejects . toThrow ( 'Content-Type mismatch' ) ;
1266+
1267+ api . destroy ( ) ;
1268+ } ) ;
1269+
1270+ it ( 'should support array of expected types' , async ( ) => {
1271+ mockFetch . mockResolvedValueOnce (
1272+ new Response ( 'plain text' , {
1273+ status : 200 ,
1274+ statusText : 'OK' ,
1275+ headers : { 'Content-Type' : 'text/plain' } ,
1276+ } )
1277+ ) ;
1278+
1279+ const api = RequestInterceptor . create ( {
1280+ baseUrl : 'https://api.example.com' ,
1281+ expectedContentType : [ 'application/json' , 'text/plain' ] ,
1282+ } ) ;
1283+
1284+ const response = await api . fetch ( '/data' ) ;
1285+ expect ( response . status ) . toBe ( 200 ) ;
1286+
1287+ api . destroy ( ) ;
1288+ } ) ;
1289+
1290+ it ( 'should call error middleware on Content-Type mismatch' , async ( ) => {
1291+ mockFetch . mockResolvedValueOnce (
1292+ new Response ( '<html></html>' , {
1293+ status : 200 ,
1294+ statusText : 'OK' ,
1295+ headers : { 'Content-Type' : 'text/html' } ,
1296+ } )
1297+ ) ;
1298+
1299+ const api = RequestInterceptor . create ( {
1300+ baseUrl : 'https://api.example.com' ,
1301+ expectedContentType : 'application/json' ,
1302+ } ) ;
1303+
1304+ const onError = vi . fn ( ) ;
1305+ api . use ( { onError } ) ;
1306+
1307+ await expect ( api . fetch ( '/page' ) ) . rejects . toThrow ( ) ;
1308+ expect ( onError ) . toHaveBeenCalled ( ) ;
1309+
1310+ api . destroy ( ) ;
1311+ } ) ;
1312+
1313+ it ( 'should allow per-request override via middleware' , async ( ) => {
1314+ mockFetch . mockResolvedValueOnce (
1315+ new Response ( '<html></html>' , {
1316+ status : 200 ,
1317+ statusText : 'OK' ,
1318+ headers : { 'Content-Type' : 'text/html' } ,
1319+ } )
1320+ ) ;
1321+
1322+ const api = RequestInterceptor . create ( {
1323+ baseUrl : 'https://api.example.com' ,
1324+ expectedContentType : 'application/json' ,
1325+ } ) ;
1326+
1327+ api . use ( {
1328+ onRequest : ( config ) => {
1329+ config . expectedContentType = 'text/html' ;
1330+ return config ;
1331+ } ,
1332+ } ) ;
1333+
1334+ const response = await api . fetch ( '/page' ) ;
1335+ expect ( response . status ) . toBe ( 200 ) ;
1336+
1337+ api . destroy ( ) ;
1338+ } ) ;
1339+
1340+ it ( 'should allow middleware to clear expectedContentType' , async ( ) => {
1341+ mockFetch . mockResolvedValueOnce (
1342+ new Response ( '<html></html>' , {
1343+ status : 200 ,
1344+ statusText : 'OK' ,
1345+ headers : { 'Content-Type' : 'text/html' } ,
1346+ } )
1347+ ) ;
1348+
1349+ const api = RequestInterceptor . create ( {
1350+ baseUrl : 'https://api.example.com' ,
1351+ expectedContentType : 'application/json' ,
1352+ } ) ;
1353+
1354+ api . use ( {
1355+ onRequest : ( config ) => {
1356+ config . expectedContentType = undefined ;
1357+ return config ;
1358+ } ,
1359+ } ) ;
1360+
1361+ const response = await api . fetch ( '/page' ) ;
1362+ expect ( response . status ) . toBe ( 200 ) ;
1363+
1364+ api . destroy ( ) ;
1365+ } ) ;
1366+ } ) ;
1367+
11731368 describe ( 'getConfig' , ( ) => {
11741369 it ( 'should return current configuration' , ( ) => {
11751370 const api = RequestInterceptor . create ( {
0 commit comments