1 | <?php |
||||||
2 | declare(strict_types=1); |
||||||
3 | |||||||
4 | namespace Lead\Router\Spec\Suite; |
||||||
5 | |||||||
6 | use Lead\Router\Exception\RouteNotFoundException; |
||||||
7 | use Lead\Router\Scope; |
||||||
8 | use stdClass; |
||||||
9 | use Lead\Router\Exception\RouterException; |
||||||
10 | use Lead\Router\Router; |
||||||
11 | use Lead\Router\Route; |
||||||
12 | |||||||
13 | describe("Route", function() { |
||||||
14 | |||||||
15 | describe("->pattern()", function() { |
||||||
16 | |||||||
17 | it("gets/sets the pattern", function() { |
||||||
18 | |||||||
19 | $route = new Route(); |
||||||
20 | expect($route->setPattern('/foo/bar/{id}[/{paths}]*'))->toBe($route); |
||||||
21 | expect($route->getPattern())->toBe('/foo/bar/{id}[/{paths}]*'); |
||||||
22 | expect($route->getRegex())->toBe('/foo/bar/([^/]+)((?:/[^/]+)*)'); |
||||||
23 | expect($route->getVariables())->toBe([ |
||||||
24 | 'id' => false, |
||||||
25 | 'paths' => '/{paths}' |
||||||
26 | ]); |
||||||
27 | |||||||
28 | }); |
||||||
29 | |||||||
30 | it("updates the regex", function() { |
||||||
31 | |||||||
32 | $route = new Route(); |
||||||
33 | expect($route->setPattern('/foo/bar/{id}[/{paths}]*'))->toBe($route); |
||||||
34 | expect($route->getRegex())->toBe('/foo/bar/([^/]+)((?:/[^/]+)*)'); |
||||||
35 | |||||||
36 | expect($route->setPattern('/foo/baz/{id}[/{paths}]*'))->toBe($route); |
||||||
37 | expect($route->getRegex())->toBe('/foo/baz/([^/]+)((?:/[^/]+)*)'); |
||||||
38 | |||||||
39 | }); |
||||||
40 | |||||||
41 | it("updates the variables", function() { |
||||||
42 | |||||||
43 | $route = new Route(); |
||||||
44 | expect($route->setPattern('/foo/bar/{id}[/{paths}]*'))->toBe($route); |
||||||
45 | expect($route->getVariables())->toBe([ |
||||||
46 | 'id' => false, |
||||||
47 | 'paths' => '/{paths}' |
||||||
48 | ]); |
||||||
49 | |||||||
50 | expect($route->setPattern('/foo/bar/{baz}[/{paths}]'))->toBe($route); |
||||||
51 | expect($route->getVariables())->toBe([ |
||||||
52 | 'baz' => false, |
||||||
53 | 'paths' => false |
||||||
54 | ]); |
||||||
55 | |||||||
56 | }); |
||||||
57 | |||||||
58 | }); |
||||||
59 | |||||||
60 | describe("->scope()", function() { |
||||||
61 | |||||||
62 | it("gets/sets route scope", function() { |
||||||
63 | |||||||
64 | $scope = new Scope(); |
||||||
65 | $route = new Route(); |
||||||
66 | expect($route->setScope($scope))->toBe($route); |
||||||
67 | expect($route->getScope())->toBe($scope); |
||||||
68 | |||||||
69 | }); |
||||||
70 | |||||||
71 | }); |
||||||
72 | |||||||
73 | describe("->methods()", function() { |
||||||
74 | |||||||
75 | it("gets/sets route methods", function() { |
||||||
76 | |||||||
77 | $route = new Route(); |
||||||
78 | expect($route->setMethods(['POST', 'PUT']))->toBe($route); |
||||||
79 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||||
80 | |||||||
81 | }); |
||||||
82 | |||||||
83 | it("formats method names", function() { |
||||||
84 | |||||||
85 | $route = new Route(); |
||||||
86 | expect($route->setMethods(['post', 'put']))->toBe($route); |
||||||
87 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||||
88 | |||||||
89 | }); |
||||||
90 | |||||||
91 | }); |
||||||
92 | |||||||
93 | describe("->allow()", function() { |
||||||
94 | |||||||
95 | it("adds some extra allowed methods", function() { |
||||||
96 | |||||||
97 | $route = new Route(['methods' => []]); |
||||||
98 | expect($route->allow(['POST', 'PUT']))->toBe($route); |
||||||
99 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||||
100 | |||||||
101 | }); |
||||||
102 | |||||||
103 | it("formats newly allowed method names", function() { |
||||||
104 | |||||||
105 | $route = new Route(['methods' => []]); |
||||||
106 | expect($route->allow(['post', 'put']))->toBe($route); |
||||||
107 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||||
108 | |||||||
109 | }); |
||||||
110 | |||||||
111 | }); |
||||||
112 | |||||||
113 | describe("->apply()", function() { |
||||||
114 | |||||||
115 | it("applies middlewares", function() { |
||||||
116 | |||||||
117 | $r = new Router(); |
||||||
118 | $route = $r->bind('foo/bar', function($route) { |
||||||
0 ignored issues
–
show
|
|||||||
119 | return 'A'; |
||||||
120 | })->apply(function($request, $response, $next) { |
||||||
0 ignored issues
–
show
The method
apply() does not exist on Lead\Router\RouteInterface . Since it exists in all sub-types, consider adding an abstract or default implementation to Lead\Router\RouteInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
121 | return '1' . $next() . '1'; |
||||||
122 | })->apply(function($request, $response, $next) { |
||||||
123 | return '2' . $next() . '2'; |
||||||
124 | }); |
||||||
125 | |||||||
126 | $route = $r->route('foo/bar'); |
||||||
127 | $actual = $route->dispatch(); |
||||||
0 ignored issues
–
show
The method
dispatch() does not exist on Lead\Router\RouteInterface . Since it exists in all sub-types, consider adding an abstract or default implementation to Lead\Router\RouteInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
128 | |||||||
129 | expect($actual)->toBe('21A12'); |
||||||
130 | |||||||
131 | }); |
||||||
132 | |||||||
133 | }); |
||||||
134 | |||||||
135 | describe("->link()", function() { |
||||||
136 | |||||||
137 | it("creates relative links", function() { |
||||||
138 | |||||||
139 | $route = new Route(['pattern' => '/foo/{bar}']); |
||||||
140 | |||||||
141 | $link = $route->link(['bar' => 'baz']); |
||||||
142 | expect($link)->toBe('/foo/baz'); |
||||||
143 | |||||||
144 | }); |
||||||
145 | |||||||
146 | it("supports optionnal parameters", function() { |
||||||
147 | |||||||
148 | $route = new Route(['pattern' => '/foo[/{bar}]']); |
||||||
149 | |||||||
150 | $link = $route->link(); |
||||||
151 | expect($link)->toBe('/foo'); |
||||||
152 | |||||||
153 | $link = $route->link(['bar' => 'baz']); |
||||||
154 | expect($link)->toBe('/foo/baz'); |
||||||
155 | |||||||
156 | }); |
||||||
157 | |||||||
158 | it("supports multiple optionnal parameters", function() { |
||||||
159 | |||||||
160 | $route = new Route(['pattern' => '/file[/{paths}]*']); |
||||||
161 | |||||||
162 | $link = $route->link(); |
||||||
163 | expect($link)->toBe('/file'); |
||||||
164 | |||||||
165 | $link = $route->link(['paths' => ['some', 'file', 'path']]); |
||||||
166 | expect($link)->toBe('/file/some/file/path'); |
||||||
167 | |||||||
168 | }); |
||||||
169 | |||||||
170 | it("merges default params", function() { |
||||||
171 | |||||||
172 | $route = new Route(['pattern' => '/foo/{bar}', 'params' => ['bar' => 'baz']]); |
||||||
173 | |||||||
174 | $link = $route->link(); |
||||||
175 | expect($link)->toBe('/foo/baz'); |
||||||
176 | |||||||
177 | }); |
||||||
178 | |||||||
179 | it("creates absolute links with custom base path", function() { |
||||||
180 | |||||||
181 | $route = new Route([ |
||||||
182 | 'pattern' => 'foo/{bar}', |
||||||
183 | 'host' => 'www.{domain}.com', |
||||||
184 | 'scheme' => 'https' |
||||||
185 | ]); |
||||||
186 | |||||||
187 | $link = $route->link([ |
||||||
188 | 'bar' => 'baz', |
||||||
189 | 'domain' => 'example' |
||||||
190 | ], [ |
||||||
191 | 'basePath' => 'app', |
||||||
192 | 'absolute' => true |
||||||
193 | ]); |
||||||
194 | expect($link)->toBe('https://www.example.com/app/foo/baz'); |
||||||
195 | |||||||
196 | }); |
||||||
197 | |||||||
198 | it("allows host and scheme overriding", function() { |
||||||
199 | |||||||
200 | $route = new Route([ |
||||||
201 | 'pattern' => 'foo/{bar}', |
||||||
202 | 'host' => 'www.{domain}.com', |
||||||
203 | 'scheme' => 'https' |
||||||
204 | ]); |
||||||
205 | |||||||
206 | $link =$route->link([ |
||||||
207 | 'bar' => 'baz', |
||||||
208 | 'domain' => 'example' |
||||||
209 | ], [ |
||||||
210 | 'scheme' => 'http', |
||||||
211 | 'host' => 'www.overrided.com', |
||||||
212 | 'basePath' => 'app', |
||||||
213 | 'absolute' => true |
||||||
214 | ]); |
||||||
215 | expect($link)->toBe('http://www.overrided.com/app/foo/baz'); |
||||||
216 | |||||||
217 | }); |
||||||
218 | |||||||
219 | it("supports repeatable parameter placeholders as an array", function() { |
||||||
220 | |||||||
221 | $route = new Route(['pattern' => 'post[/{id}]*']); |
||||||
222 | expect($route->link(['id' => '123']))->toBe('/post/123'); |
||||||
223 | expect($route->link(['id' => ['123']]))->toBe('/post/123'); |
||||||
224 | expect($route->link(['id' => ['123', '456', '789']]))->toBe('/post/123/456/789'); |
||||||
225 | expect($route->link([]))->toBe('/post'); |
||||||
226 | |||||||
227 | $route = new Route(['pattern' => '/post[/{id}]+']); |
||||||
228 | expect($route->link(['id' => ['123']]))->toBe('/post/123'); |
||||||
229 | expect($route->link(['id' => ['123', '456', '789']]))->toBe('/post/123/456/789'); |
||||||
230 | |||||||
231 | }); |
||||||
232 | |||||||
233 | it("supports route with multiple optional segments", function() { |
||||||
234 | |||||||
235 | $route = new Route(['pattern' => '[{relation}/{rid:[^/:][^/]*}/]post[/{id:[^/:][^/]*}][/:{action}]']); |
||||||
236 | |||||||
237 | $link = $route->link(); |
||||||
238 | expect($link)->toBe('/post'); |
||||||
239 | |||||||
240 | $link = $route->link(['action' => 'add']); |
||||||
241 | expect($link)->toBe('/post/:add'); |
||||||
242 | |||||||
243 | $link = $route->link(['action' => 'edit', 'id' => 12]); |
||||||
244 | expect($link)->toBe('/post/12/:edit'); |
||||||
245 | |||||||
246 | $link = $route->link(['relation' => 'user', 'rid' => 5]); |
||||||
247 | expect($link)->toBe('/user/5/post'); |
||||||
248 | |||||||
249 | $link = $route->link(['relation' => 'user', 'rid' => 5, 'action' => 'edit', 'id' => 12]); |
||||||
250 | expect($link)->toBe('/user/5/post/12/:edit'); |
||||||
251 | |||||||
252 | }); |
||||||
253 | |||||||
254 | it("supports route with complex repeatable optional segments", function() { |
||||||
255 | |||||||
256 | $route = new Route(['pattern' => '[{relations:[^/]+/[^/:][^/]*}/]*post[/{id:[^/:][^/]*}][/:{action}]']); |
||||||
257 | |||||||
258 | $link = $route->link(); |
||||||
259 | expect($link)->toBe('/post'); |
||||||
260 | |||||||
261 | $link = $route->link(['action' => 'add']); |
||||||
262 | expect($link)->toBe('/post/:add'); |
||||||
263 | |||||||
264 | $link = $route->link(['action' => 'edit', 'id' => 12]); |
||||||
265 | expect($link)->toBe('/post/12/:edit'); |
||||||
266 | |||||||
267 | $link = $route->link(['relations' => [['user', 5]]]); |
||||||
268 | expect($link)->toBe('/user/5/post'); |
||||||
269 | |||||||
270 | $link = $route->link(['relations' => [['user', 5]], 'action' => 'edit', 'id' => 12]); |
||||||
271 | expect($link)->toBe('/user/5/post/12/:edit'); |
||||||
272 | |||||||
273 | }); |
||||||
274 | |||||||
275 | it("throws an exception for missing variables", function() { |
||||||
276 | |||||||
277 | $closure = function() { |
||||||
278 | $route = new Route(['pattern' => 'post[/{id}]+']); |
||||||
279 | echo $route->link([]); |
||||||
280 | }; |
||||||
281 | expect($closure)->toThrow(new RouterException("Missing parameters `'id'` for route: `'#/post[/{id}]+'`.")); |
||||||
282 | |||||||
283 | }); |
||||||
284 | |||||||
285 | it("throws an exception when a variable doesn't match its capture pattern", function() { |
||||||
286 | |||||||
287 | $closure = function() { |
||||||
288 | $route = new Route(['pattern' => 'post/{id:[0-9]{3}}']); |
||||||
289 | $route->link(['id' => '1234']); |
||||||
290 | }; |
||||||
291 | expect($closure)->toThrow(new RouterException("Expected `'id'` to match `'[0-9]{3}'`, but received `'1234'`.")); |
||||||
292 | |||||||
293 | }); |
||||||
294 | |||||||
295 | it("throws an exception when a an array is provided for a non repeatable parameter placeholder", function() { |
||||||
296 | |||||||
297 | $closure = function() { |
||||||
298 | $route = new Route(['pattern' => 'post/{id}']); |
||||||
299 | $route->link(['id' => ['123', '456']]); |
||||||
300 | }; |
||||||
301 | expect($closure)->toThrow(new RouterException("Expected `'id'` to match `'[^/]+'`, but received `'123/456'`.")); |
||||||
302 | |||||||
303 | }); |
||||||
304 | |||||||
305 | it("throws an exception when one element of an array doesn't match the capture pattern", function() { |
||||||
306 | |||||||
307 | $closure = function() { |
||||||
308 | $route = new Route(['pattern' => 'post[/{id:[0-9]{3}}]+']); |
||||||
309 | $route->link(['id' => ['123', '456', '78']]); |
||||||
310 | }; |
||||||
311 | expect($closure)->toThrow(new RouterException("Expected `'id'` to match `'[0-9]{3}'`, but received `'78'`.")); |
||||||
312 | |||||||
313 | }); |
||||||
314 | |||||||
315 | }); |
||||||
316 | |||||||
317 | describe("->dispatch()", function() { |
||||||
318 | |||||||
319 | it("passes route as argument of the handler function", function() { |
||||||
320 | |||||||
321 | $r = new Router(); |
||||||
322 | $r->get('foo/{var1}[/{var2}]', |
||||||
0 ignored issues
–
show
The method
get() does not exist on Lead\Router\Router . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
323 | ['host' => '{subdomain}.{domain}.bar'], |
||||||
324 | function($route, $response) { |
||||||
325 | return array_merge([$response], array_values($route->params)); |
||||||
326 | } |
||||||
327 | ); |
||||||
328 | |||||||
329 | $response = new stdClass(); |
||||||
330 | |||||||
331 | $route = $r->route('foo/25', 'GET', 'foo.biz.bar'); |
||||||
332 | $actual = $route->dispatch($response); |
||||||
333 | expect($actual)->toBe([$response, 'foo', 'biz', '25', null]); |
||||||
334 | |||||||
335 | $route = $r->route('foo/25/bar', 'GET', 'foo.biz.bar'); |
||||||
336 | $actual = $route->dispatch($response); |
||||||
337 | expect($actual)->toBe([$response, 'foo', 'biz', '25', 'bar']); |
||||||
338 | |||||||
339 | }); |
||||||
340 | |||||||
341 | it("throws an exception on non valid routes", function() { |
||||||
342 | |||||||
343 | $closure = function() { |
||||||
344 | $r = new Router(); |
||||||
345 | $r->get('foo', function() {}); |
||||||
346 | $route = $r->route('bar'); |
||||||
347 | $route->dispatch(); |
||||||
348 | }; |
||||||
349 | |||||||
350 | expect($closure)->toThrow(new RouteNotFoundException("No route found for `*:*:GET:/bar`.", 404)); |
||||||
351 | }); |
||||||
352 | |||||||
353 | }); |
||||||
354 | |||||||
355 | }); |
||||||
356 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.