git.iko.soy/rowtype-yoga/purescript-yoga-fetch-om
on main help sign in
167 lines 6.2 kB history raw
1module BuildUrl.Spec where
3import Prelude
5import Data.Maybe (Maybe(..))
6import Test.Spec (Spec, describe, it)
7import Test.Spec.Assertions (shouldEqual)
8import Type.Proxy (Proxy(..))
9import Unsafe.Coerce (unsafeCoerce)
10import Yoga.Fetch.Om.BuildUrl (buildUrl, substitutePathParams, appendQueryParams)
11import Yoga.HTTP.API.Path (Path, type (/), type (:))
13--------------------------------------------------------------------------------
14-- SubstitutePathParams Tests
15--------------------------------------------------------------------------------
17spec :: Spec Unit
18spec = do
19 describe "substitutePathParams" do
20 it "substitutes single path param" do
21 let result = substitutePathParams @(id :: Int) { id: 42 } "/users/:id"
22 result `shouldEqual` "/users/42"
24 it "substitutes multiple path params" do
25 let
26 result = substitutePathParams @(userId :: Int, postId :: Int)
27 { userId: 1, postId: 99 }
28 "/users/:userId/posts/:postId"
29 result `shouldEqual` "/users/1/posts/99"
31 it "handles String params" do
32 let
33 result = substitutePathParams @(slug :: String)
34 { slug: "hello-world" }
35 "/posts/:slug"
36 result `shouldEqual` "/posts/hello-world"
37 it "URL-encodes reserved characters in path params" do
38 let
39 result = substitutePathParams @(slug :: String)
40 { slug: "a/b c?d#e" }
41 "/users/:slug"
42 result `shouldEqual` "/users/a%2Fb%20c%3Fd%23e"
44 it "distinguishes path param names with shared prefixes" do
45 let
46 result = substitutePathParams @(id :: Int, id2 :: Int)
47 { id: 1, id2: 2 }
48 "/pairs/:id/:id2"
49 result `shouldEqual` "/pairs/1/2"
51 it "handles empty params" do
52 let result = substitutePathParams @() {} "/users"
53 result `shouldEqual` "/users"
55 describe "appendQueryParams" do
56 it "appends single query param" do
57 let result = appendQueryParams @(limit :: Int) { limit: 10 } "/users"
58 result `shouldEqual` "/users?limit=10"
60 it "appends multiple query params" do
61 let
62 result = appendQueryParams @(limit :: Int, offset :: Int)
63 { limit: 10, offset: 20 }
64 "/users"
65 result `shouldEqual` "/users?limit=10&offset=20"
67 it "handles optional params - provided via partial record" do
68 let
69 result = appendQueryParams @(limit :: Maybe Int)
70 (unsafeCoerce { limit: 10 } :: Record (limit :: Maybe Int))
71 "/users"
72 result `shouldEqual` "/users?limit=10"
74 it "serializes a present explicit Maybe query param" do
75 let result = appendQueryParams @(id :: Maybe Int) { id: Just 42 } "/users"
76 result `shouldEqual` "/users?id=42"
78 it "omits an explicit Nothing query param" do
79 let result = appendQueryParams @(id :: Maybe Int) { id: Nothing } "/users"
80 result `shouldEqual` "/users"
82 it "handles optional params - omitted via partial record" do
83 let
84 result = appendQueryParams @(limit :: Maybe Int)
85 (unsafeCoerce {} :: Record (limit :: Maybe Int))
86 "/users"
87 result `shouldEqual` "/users"
89 it "handles mixed optional and required params" do
90 let
91 result = appendQueryParams @(limit :: Maybe Int, offset :: Int)
92 (unsafeCoerce { offset: 20 } :: Record (limit :: Maybe Int, offset :: Int))
93 "/users"
94 result `shouldEqual` "/users?offset=20"
96 it "handles empty query params" do
97 let result = appendQueryParams @() {} "/users"
98 result `shouldEqual` "/users"
100 it "URL-encodes special characters in values" do
101 let result = appendQueryParams @(search :: String) { search: "hello world&more=yes" } "/users"
102 result `shouldEqual` "/users?search=hello%20world%26more%3Dyes"
103 it "appends query params to an existing query string" do
104 let result = appendQueryParams @(search :: String) { search: "new value" } "/users?token=old"
105 result `shouldEqual` "/users?token=old&search=new%20value"
107 it "inserts query params before a URL fragment" do
108 let result = appendQueryParams @(search :: String) { search: "new value" } "/users#details"
109 result `shouldEqual` "/users?search=new%20value#details"
111 it "repeats Array query params one pair per element" do
112 let
113 result = appendQueryParams @(categories :: Array Int)
114 { categories: [ 3030, 103130 ] }
115 "/users"
116 result `shouldEqual` "/users?categories=3030&categories=103130"
118 it "omits empty Array query params" do
119 let
120 result = appendQueryParams @(categories :: Array Int)
121 { categories: [] }
122 "/users"
123 result `shouldEqual` "/users"
125 it "mixes Array and bare query params" do
126 let
127 result = appendQueryParams @(query :: String, categories :: Array Int)
128 { query: "foo", categories: [ 1, 2 ] }
129 "/users"
130 result `shouldEqual` "/users?categories=1&categories=2&query=foo"
132 describe "buildUrl" do
133 it "builds URL with path params only" do
134 let
135 result = buildUrl
136 "https://api.example.com"
137 (Proxy :: _ (Path ("users" / "id" : Int)))
138 { id: 42 }
139 {}
140 result `shouldEqual` "https://api.example.com/users/42"
142 it "builds URL with query params only" do
143 let
144 result = buildUrl
145 "https://api.example.com"
146 (Proxy :: _ (Path "users"))
147 {}
148 { limit: 10, offset: 20 }
149 result `shouldEqual` "https://api.example.com/users?limit=10&offset=20"
151 it "appends route paths before base URL query strings and fragments" do
152 let
153 result = buildUrl
154 "https://api.example.com/base/?token=old#details"
155 (Proxy :: _ (Path "users"))
156 {}
157 { search: "new value" }
158 result `shouldEqual` "https://api.example.com/base/users?token=old&search=new%20value#details"
160 it "does not substitute path captures inside a base URL query string" do
161 let
162 result = buildUrl
163 "https://api.example.com?redirect=/:id#details"
164 (Proxy :: _ (Path ("users" / "id" : Int)))
165 { id: 42 }
166 {}
167 result `shouldEqual` "https://api.example.com/users/42?redirect=/:id#details"