1
1
import { HttpResponse , type StrictResponse } from "msw" ;
2
2
import { afterAll , beforeAll , describe , expect , expectTypeOf , it } from "vitest" ;
3
3
import createClient , {
4
+ type FetchOptions ,
5
+ type HeadersOptions ,
4
6
type Middleware ,
5
7
type MiddlewareCallbackParams ,
6
8
type QuerySerializerOptions ,
7
9
} from "../src/index.js" ;
8
- import { server , baseUrl , useMockRequestHandler , toAbsoluteURL } from "./fixtures/mock-server.js" ;
10
+ import { baseUrl , server , toAbsoluteURL , useMockRequestHandler } from "./fixtures/mock-server.js" ;
9
11
import type { paths } from "./fixtures/api.js" ;
10
12
11
13
beforeAll ( ( ) => {
@@ -784,12 +786,7 @@ describe("client", () => {
784
786
await client . GET ( "/self" ) ;
785
787
786
788
// assert default headers were passed
787
- expect ( getRequest ( ) . headers ) . toEqual (
788
- new Headers ( {
789
- ...headers , // assert new header got passed
790
- "Content-Type" : "application/json" , // probably doesn’t need to get tested, but this was simpler than writing lots of code to ignore these
791
- } ) ,
792
- ) ;
789
+ expect ( getRequest ( ) . headers ) . toEqual ( new Headers ( headers ) ) ;
793
790
} ) ;
794
791
795
792
it ( "can be overridden" , async ( ) => {
@@ -815,7 +812,6 @@ describe("client", () => {
815
812
expect ( getRequest ( ) . headers ) . toEqual (
816
813
new Headers ( {
817
814
"Cache-Control" : "no-cache" ,
818
- "Content-Type" : "application/json" ,
819
815
} ) ,
820
816
) ;
821
817
} ) ;
@@ -859,6 +855,139 @@ describe("client", () => {
859
855
} ) ;
860
856
} ) ;
861
857
858
+ describe ( "content-type" , ( ) => {
859
+ const BODY_ACCEPTING_METHODS = [ [ "PUT" ] , [ "POST" ] , [ "DELETE" ] , [ "OPTIONS" ] , [ "PATCH" ] ] as const ;
860
+ const ALL_METHODS = [ ...BODY_ACCEPTING_METHODS , [ "GET" ] , [ "HEAD" ] ] as const ;
861
+
862
+ const fireRequestAndGetContentType = async ( options : {
863
+ defaultHeaders ?: HeadersOptions ;
864
+ method : ( typeof ALL_METHODS ) [ number ] [ number ] ;
865
+ fetchOptions : FetchOptions < any > ;
866
+ } ) => {
867
+ const client = createClient < any > ( { baseUrl, headers : options . defaultHeaders } ) ;
868
+ const { getRequest } = useMockRequestHandler ( {
869
+ baseUrl,
870
+ method : "all" ,
871
+ path : "/blogposts-optional" ,
872
+ status : 200 ,
873
+ } ) ;
874
+ await client [ options . method ] ( "/blogposts-optional" , options . fetchOptions as any ) ;
875
+
876
+ const request = getRequest ( ) ;
877
+ return request . headers . get ( "content-type" ) ;
878
+ } ;
879
+
880
+ it . each ( ALL_METHODS ) ( "no content-type for body-less requests - %s" , async ( method ) => {
881
+ const contentType = await fireRequestAndGetContentType ( {
882
+ method,
883
+ fetchOptions : { } ,
884
+ } ) ;
885
+
886
+ expect ( contentType ) . toBe ( null ) ;
887
+ } ) ;
888
+
889
+ it . each ( ALL_METHODS ) ( "no content-type for `undefined` body requests - %s" , async ( method ) => {
890
+ const contentType = await fireRequestAndGetContentType ( {
891
+ method,
892
+ fetchOptions : {
893
+ body : undefined ,
894
+ } ,
895
+ } ) ;
896
+
897
+ expect ( contentType ) . toBe ( null ) ;
898
+ } ) ;
899
+
900
+ const BODIES = [ { prop : "a" } , { } , "" , "str" , null , false , 0 , 1 , new Date ( "2024-08-07T09:52:00.836Z" ) ] as const ;
901
+ // const BODIES = ["str"] as const;
902
+ const METHOD_BODY_COMBINATIONS = BODY_ACCEPTING_METHODS . flatMap ( ( [ method ] ) =>
903
+ BODIES . map ( ( body ) => [ method , body ] as const ) ,
904
+ ) ;
905
+
906
+ it . each ( METHOD_BODY_COMBINATIONS ) (
907
+ "implicit default content-type for body-full requests - %s, %j" ,
908
+ async ( method , body ) => {
909
+ const contentType = await fireRequestAndGetContentType ( {
910
+ method,
911
+ fetchOptions : {
912
+ body,
913
+ } ,
914
+ } ) ;
915
+
916
+ expect ( contentType ) . toBe ( "application/json" ) ;
917
+ } ,
918
+ ) ;
919
+
920
+ it . each ( METHOD_BODY_COMBINATIONS ) (
921
+ "provided default content-type for body-full requests - %s, %j" ,
922
+ async ( method , body ) => {
923
+ const contentType = await fireRequestAndGetContentType ( {
924
+ defaultHeaders : {
925
+ "content-type" : "application/my-json" ,
926
+ } ,
927
+ method,
928
+ fetchOptions : {
929
+ body,
930
+ } ,
931
+ } ) ;
932
+
933
+ expect ( contentType ) . toBe ( "application/my-json" ) ;
934
+ } ,
935
+ ) ;
936
+
937
+ it . each ( METHOD_BODY_COMBINATIONS ) (
938
+ "native-fetch default content-type for body-full requests, when default is suppressed - %s, %j" ,
939
+ async ( method , body ) => {
940
+ const contentType = await fireRequestAndGetContentType ( {
941
+ defaultHeaders : {
942
+ "content-type" : null ,
943
+ } ,
944
+ method,
945
+ fetchOptions : {
946
+ body,
947
+ } ,
948
+ } ) ;
949
+ // the fetch implementation won't allow sending a body without content-type,
950
+ // so it invents one up and sends it, hopefully this will be consistent across
951
+ // local environments and won't make the tests flaky
952
+ expect ( contentType ) . toBe ( "text/plain;charset=UTF-8" ) ;
953
+ } ,
954
+ ) ;
955
+
956
+ it . each ( METHOD_BODY_COMBINATIONS ) (
957
+ "specified content-type for body-full requests - %s, %j" ,
958
+ async ( method , body ) => {
959
+ const contentType = await fireRequestAndGetContentType ( {
960
+ method,
961
+ fetchOptions : {
962
+ body,
963
+ headers : {
964
+ "content-type" : "application/my-json" ,
965
+ } ,
966
+ } ,
967
+ } ) ;
968
+
969
+ expect ( contentType ) . toBe ( "application/my-json" ) ;
970
+ } ,
971
+ ) ;
972
+
973
+ it . each ( METHOD_BODY_COMBINATIONS ) (
974
+ "specified content-type for body-full requests, even when default is suppressed - %s, %j" ,
975
+ async ( method , body ) => {
976
+ const contentType = await fireRequestAndGetContentType ( {
977
+ method,
978
+ fetchOptions : {
979
+ body,
980
+ headers : {
981
+ "content-type" : "application/my-json" ,
982
+ } ,
983
+ } ,
984
+ } ) ;
985
+
986
+ expect ( contentType ) . toBe ( "application/my-json" ) ;
987
+ } ,
988
+ ) ;
989
+ } ) ;
990
+
862
991
describe ( "fetch" , ( ) => {
863
992
it ( "createClient" , async ( ) => {
864
993
function createCustomFetch ( data : any ) {
0 commit comments