1 | <?php |
||||
2 | declare(strict_types=1); |
||||
3 | |||||
4 | namespace Lead\Router\Spec\Suite; |
||||
5 | |||||
6 | use Kahlan\Plugin\Double; |
||||
7 | use Lead\Router\Exception\RouteNotFoundException; |
||||
8 | use Lead\Router\Exception\RouterException; |
||||
9 | use Lead\Router\Router; |
||||
10 | use Lead\Router\Route; |
||||
11 | use Psr\Http\Message\ServerRequestInterface; |
||||
12 | use Psr\Http\Message\UriInterface; |
||||
13 | |||||
14 | describe("Router", function() { |
||||
15 | |||||
16 | beforeEach(function() { |
||||
17 | |||||
18 | $this->router = new Router(); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||||
19 | $this->export = function($request) { |
||||
20 | return array_intersect_key($request, array_fill_keys(['path', 'method', 'host', 'scheme'], true)); |
||||
21 | }; |
||||
22 | |||||
23 | }); |
||||
24 | |||||
25 | describe("->__construct()", function() { |
||||
26 | |||||
27 | it("formats the basePath", function() { |
||||
28 | |||||
29 | $router = new Router(['basePath' => '/']); |
||||
30 | expect($router->getBasePath())->toBe(''); |
||||
31 | |||||
32 | }); |
||||
33 | |||||
34 | }); |
||||
35 | |||||
36 | describe("->getBasePath()", function() { |
||||
37 | |||||
38 | it("sets an empty basePath", function() { |
||||
39 | |||||
40 | expect($this->router->setBasePath('/'))->toBe($this->router); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
41 | expect($this->router->getBasePath())->toBe(''); |
||||
42 | |||||
43 | expect($this->router->setBasePath(''))->toBe($this->router); |
||||
44 | expect($this->router->getBasePath())->toBe(''); |
||||
45 | |||||
46 | }); |
||||
47 | |||||
48 | it("adds an leading slash for non empty basePath", function() { |
||||
49 | |||||
50 | expect($this->router->setBasePath('app'))->toBe($this->router); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
51 | expect($this->router->getBasePath())->toBe('/app'); |
||||
52 | |||||
53 | expect($this->router->setBasePath('/base'))->toBe($this->router); |
||||
54 | expect($this->router->getBasePath())->toBe('/base'); |
||||
55 | |||||
56 | }); |
||||
57 | |||||
58 | }); |
||||
59 | |||||
60 | describe("->bind()", function() { |
||||
61 | |||||
62 | it("binds a named route", function() { |
||||
63 | |||||
64 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
65 | $route = $r->bind('foo/bar', ['name' => 'foo'], function() { return 'hello'; }); |
||||
66 | expect(isset($r['foo']))->toBe(true); |
||||
67 | expect($r['foo'])->toBe($route); |
||||
68 | |||||
69 | }); |
||||
70 | |||||
71 | it("matches on methods", function() { |
||||
72 | |||||
73 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
74 | $r->bind('foo/bar', ['methods' => ['POST', 'PUT']], function () {}); |
||||
75 | |||||
76 | $route = $r->route('foo/bar', 'POST'); |
||||
77 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||
78 | |||||
79 | $route = $r->route('foo/bar', 'PUT'); |
||||
80 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||
81 | |||||
82 | try { |
||||
83 | $route = $r->route('bar/foo', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
84 | } catch (RouteNotFoundException $e) { |
||||
85 | expect($e->getMessage())->toBe("No route found for `*:*:GET:/bar/foo`."); |
||||
86 | } |
||||
87 | }); |
||||
88 | |||||
89 | it("supports lowercase method names", function() { |
||||
90 | |||||
91 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
92 | $r->bind('foo/bar', ['methods' => ['POST', 'PUT']], function () {}); |
||||
93 | |||||
94 | $route = $r->route('foo/bar', 'post'); |
||||
95 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||
96 | |||||
97 | $route = $r->route('foo/bar', 'put'); |
||||
98 | expect($route->getMethods())->toBe(['POST', 'PUT']); |
||||
99 | |||||
100 | try { |
||||
101 | $route = $r->route('bar/foo', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
102 | } catch (RouteNotFoundException $e) { |
||||
103 | expect($e->getMessage())->toBe("No route found for `*:*:GET:/bar/foo`."); |
||||
104 | } |
||||
105 | }); |
||||
106 | |||||
107 | it("matches on same path different methods", function() { |
||||
108 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
109 | $r->bind('foo/bar', ['name' => 'foo', 'methods' => ['POST']], function () {}); |
||||
110 | $r->bind('foo/bar', ['name' => 'bar', 'methods' => ['PUT']], function () {}); |
||||
111 | |||||
112 | $route = $r->route('foo/bar', 'POST'); |
||||
113 | expect($route->name)->toBe('foo'); |
||||
114 | |||||
115 | $route = $r->route('foo/bar', 'PUT'); |
||||
116 | expect($route->name)->toBe('bar'); |
||||
117 | }); |
||||
118 | |||||
119 | it("throws an exception when the handler is not a closure", function() { |
||||
120 | |||||
121 | $closure = function() { |
||||
122 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
123 | $r->bind('foo', 'substr'); |
||||
124 | }; |
||||
125 | |||||
126 | expect($closure)->toThrow(new RouterException("The handler needs to be an instance of `Closure` or implements the `__invoke()` magic method.")); |
||||
127 | |||||
128 | }); |
||||
129 | |||||
130 | it("throws an exception when trying to use the `'method'` option", function() { |
||||
131 | |||||
132 | $closure = function() { |
||||
133 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
134 | $r->bind('foo', ['method' => 'GET'], function() {}); |
||||
135 | }; |
||||
136 | |||||
137 | expect($closure)->toThrow(new RouterException("Use the `'methods'` option to limit HTTP verbs on a route binding definition.")); |
||||
138 | |||||
139 | }); |
||||
140 | |||||
141 | }); |
||||
142 | |||||
143 | describe("->link()", function() { |
||||
144 | |||||
145 | it("forwards router base path", function() { |
||||
146 | |||||
147 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
148 | $r->basePath('app'); |
||||
149 | |||||
150 | $r->group(['host' => 'www.{domain}.com', 'scheme' => 'https'], function($r) { |
||||
151 | $r->bind('foo/{bar}', ['name' => 'foo'], function () {}); |
||||
152 | }); |
||||
153 | |||||
154 | $link = $r->link('foo', [ |
||||
155 | 'bar' => 'baz', |
||||
156 | 'domain' => 'example' |
||||
157 | ], ['absolute' => true]); |
||||
158 | expect($link)->toBe('https://www.example.com/app/foo/baz'); |
||||
159 | |||||
160 | }); |
||||
161 | |||||
162 | it("maintains prefixes in nested routes", function() { |
||||
163 | |||||
164 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
165 | $r->group('foo', ['name' => 'foz'], function($r) { |
||||
166 | $r->group('bar', ['name' => 'baz'], function($r) { |
||||
167 | $r->bind('{var1}', ['name' => 'quz'], function () {}); |
||||
168 | }); |
||||
169 | }); |
||||
170 | |||||
171 | $link = $r->link('foz.baz.quz', ['var1' => 'hello']); |
||||
172 | expect($link)->toBe('/foo/bar/hello'); |
||||
173 | |||||
174 | }); |
||||
175 | |||||
176 | it("persists persisted parameters in a dispatching context", function() { |
||||
177 | |||||
178 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
179 | $r->group('{locale:en|fr}', ['persist' => 'locale'], function($r) { |
||||
180 | $r->bind('{controller}/{action}[/{id}]', ['name' => 'controller'], function () {}); |
||||
181 | }); |
||||
182 | |||||
183 | $r->route('fr/post/index'); |
||||
184 | $link = $r->link('controller', [ |
||||
185 | 'controller' => 'post', |
||||
186 | 'action' => 'view', |
||||
187 | 'id' => 5 |
||||
188 | ]); |
||||
189 | expect($link)->toBe('/fr/post/view/5'); |
||||
190 | |||||
191 | $r->route('en/post/index'); |
||||
192 | $link = $r->link('controller', [ |
||||
193 | 'controller' => 'post', |
||||
194 | 'action' => 'view', |
||||
195 | 'id' => 5 |
||||
196 | ]); |
||||
197 | expect($link)->toBe('/en/post/view/5'); |
||||
198 | |||||
199 | }); |
||||
200 | |||||
201 | it("overrides persisted parameters in a dispatching context", function() { |
||||
202 | |||||
203 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
204 | $r->group('{locale:en|fr}', ['persist' => 'locale'], function($r) { |
||||
205 | $r->bind('{controller}/{action}[/{id}]', ['name' => 'controller'], function () {}); |
||||
206 | }); |
||||
207 | |||||
208 | $r->route('fr/post/index'); |
||||
209 | |||||
210 | $link = $r->link('controller', [ |
||||
211 | 'locale' => 'en', |
||||
212 | 'controller' => 'post', |
||||
213 | 'action' => 'view', |
||||
214 | 'id' => 5 |
||||
215 | ]); |
||||
216 | expect($link)->toBe('/en/post/view/5'); |
||||
217 | |||||
218 | }); |
||||
219 | |||||
220 | it("throws an exception when no route is found for a specified name", function() { |
||||
221 | |||||
222 | $closure = function() { |
||||
223 | $this->router->link('not.binded.yet', []); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
224 | }; |
||||
225 | |||||
226 | expect($closure)->toThrow(new RouterException("No binded route defined for `'not.binded.yet'`, bind it first with `bind()`.")); |
||||
227 | |||||
228 | }); |
||||
229 | }); |
||||
230 | |||||
231 | describe("->route()", function() { |
||||
232 | |||||
233 | it("routes on a simple route", function() { |
||||
234 | |||||
235 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
236 | $r->bind('foo/bar', function () {}); |
||||
237 | |||||
238 | $route = $r->route('foo/bar', 'GET'); |
||||
239 | expect($this->export($route->request))->toEqual([ |
||||
240 | 'host' => '*', |
||||
241 | 'scheme' => '*', |
||||
242 | 'method' => 'GET', |
||||
243 | 'path' => 'foo/bar' |
||||
244 | ]); |
||||
245 | |||||
246 | try { |
||||
247 | $route = $r->route('bar/foo', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
248 | } catch (RouteNotFoundException $e) { |
||||
249 | expect($e->getMessage())->toBe("No route found for `*:*:GET:/bar/foo`."); |
||||
250 | } |
||||
251 | }); |
||||
252 | |||||
253 | it("routes on a named route", function() { |
||||
254 | |||||
255 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
256 | $r->bind('foo/bar', ['name' => 'foo'], function () {}); |
||||
257 | |||||
258 | $route = $r->route('foo/bar', 'GET'); |
||||
259 | expect($route->name)->toBe('foo'); |
||||
260 | |||||
261 | try { |
||||
262 | $route = $r->route('bar/foo', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
263 | } catch (RouteNotFoundException $e) { |
||||
264 | expect($e->getMessage())->toBe("No route found for `*:*:GET:/bar/foo`."); |
||||
265 | } |
||||
266 | }); |
||||
267 | |||||
268 | it("supports empty as index route", function() { |
||||
269 | |||||
270 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
271 | $r->bind('', function () {}); |
||||
272 | |||||
273 | $route = $r->route('', 'GET'); |
||||
274 | expect($this->export($route->request))->toEqual([ |
||||
275 | 'host' => '*', |
||||
276 | 'scheme' => '*', |
||||
277 | 'method' => 'GET', |
||||
278 | 'path' => '' |
||||
279 | ]); |
||||
280 | |||||
281 | }); |
||||
282 | |||||
283 | it("supports a slash as indes route", function() { |
||||
284 | |||||
285 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
286 | $r->bind('/', function () {}); |
||||
287 | |||||
288 | $route = $r->route('', 'GET'); |
||||
289 | expect($this->export($route->request))->toEqual([ |
||||
290 | 'host' => '*', |
||||
291 | 'scheme' => '*', |
||||
292 | 'method' => 'GET', |
||||
293 | 'path' => '' |
||||
294 | ]); |
||||
295 | |||||
296 | }); |
||||
297 | |||||
298 | it("supports route variables", function() { |
||||
299 | |||||
300 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
301 | $r->get('foo/{param}', function() {}); |
||||
302 | |||||
303 | $route = $r->route('foo/bar', 'GET'); |
||||
304 | expect($route->params)->toBe(['param' => 'bar']); |
||||
305 | |||||
306 | try { |
||||
307 | $route = $r->route('bar/foo', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
308 | } catch (RouteNotFoundException $e) { |
||||
309 | expect($e->getMessage())->toBe("No route found for `*:*:GET:/bar/foo`."); |
||||
310 | } |
||||
311 | }); |
||||
312 | |||||
313 | it("supports constrained route variables", function() { |
||||
314 | |||||
315 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
316 | $r->get('foo/{var1:\d+}', function() {}); |
||||
317 | |||||
318 | $route = $r->route('foo/25', 'GET'); |
||||
319 | expect($route->params)->toBe(['var1' => '25']); |
||||
320 | |||||
321 | try { |
||||
322 | $route = $r->route('foo/bar', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
323 | } catch (RouteNotFoundException $e) { |
||||
324 | expect($e->getMessage())->toBe("No route found for `*:*:GET:/foo/bar`."); |
||||
325 | } |
||||
326 | }); |
||||
327 | |||||
328 | it("supports optional segments with variables", function() { |
||||
329 | |||||
330 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
331 | $r->get('foo[/{var1}]', function() {}); |
||||
332 | |||||
333 | $route = $r->route('foo', 'GET'); |
||||
334 | expect($route->params)->toBe(['var1' => null]); |
||||
335 | |||||
336 | $route = $r->route('foo/bar', 'GET'); |
||||
337 | expect($route->params)->toBe(['var1' => 'bar']); |
||||
338 | |||||
339 | }); |
||||
340 | |||||
341 | it("supports repeatable segments", function() { |
||||
342 | |||||
343 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
344 | $r->get('foo[/:{var1}]*[/bar[/:{var2}]*]', function() {}); |
||||
345 | |||||
346 | $route = $r->route('foo', 'GET'); |
||||
347 | expect($route->params)->toBe([ |
||||
348 | 'var1' => [], |
||||
349 | 'var2' => [] |
||||
350 | ]); |
||||
351 | |||||
352 | $route = $r->route('foo/:bar', 'GET'); |
||||
353 | expect($route->params)->toBe([ |
||||
354 | 'var1' => ['bar'], |
||||
355 | 'var2' => [] |
||||
356 | ]); |
||||
357 | |||||
358 | $route = $r->route('foo/:bar/:baz/bar/:fuz', 'GET'); |
||||
359 | expect($route->params)->toBe([ |
||||
360 | 'var1' => ['bar', 'baz'], |
||||
361 | 'var2' => ['fuz'] |
||||
362 | ]); |
||||
363 | }); |
||||
364 | |||||
365 | it("supports optional segments with custom variable regex", function() { |
||||
366 | |||||
367 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
368 | $r->get('foo[/{var1:\d+}]', function() {}); |
||||
369 | |||||
370 | $route = $r->route('foo', 'GET'); |
||||
371 | expect($route->params)->toBe(['var1' => null]); |
||||
372 | |||||
373 | $route = $r->route('foo/25', 'GET'); |
||||
374 | expect($route->params)->toBe(['var1' => '25']); |
||||
375 | |||||
376 | try { |
||||
377 | $route = $r->route('foo/baz', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
378 | } catch (RouteNotFoundException $e) { |
||||
379 | expect($e->getMessage())->toBe("No route found for `*:*:GET:/foo/baz`."); |
||||
380 | } |
||||
381 | }); |
||||
382 | |||||
383 | it("supports multiple optional segments", function() { |
||||
384 | |||||
385 | $patterns = [ |
||||
386 | '/[{var1}[/{var2}]]' |
||||
387 | ]; |
||||
388 | |||||
389 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
390 | |||||
391 | foreach ($patterns as $pattern) { |
||||
392 | $r->get($pattern, function() {}); |
||||
393 | |||||
394 | $route = $r->route('foo', 'GET'); |
||||
395 | expect($route->params)->toBe(['var1' => 'foo', 'var2' => null]); |
||||
396 | |||||
397 | $route = $r->route('foo/bar', 'GET'); |
||||
398 | expect($route->params)->toBe(['var1' => 'foo', 'var2' => 'bar']); |
||||
399 | |||||
400 | $r->clear(); |
||||
401 | |||||
402 | }; |
||||
403 | |||||
404 | }); |
||||
405 | |||||
406 | it("supports host variables", function() { |
||||
407 | |||||
408 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
409 | $r->get('foo/{var1:\d+}', ['host' => 'foo.{domain}.bar'], function() {}); |
||||
410 | $r->get('foo/{var1:\d+}', ['host' => 'foo.{domain}.baz'], function() {}); |
||||
411 | |||||
412 | $route = $r->route('foo/25', 'GET', 'foo.biz.bar'); |
||||
413 | expect($route->getHost()->getPattern())->toBe('foo.{domain}.bar'); |
||||
414 | expect($route->params)->toBe(['domain' => 'biz', 'var1' => '25']); |
||||
415 | |||||
416 | $route = $r->route('foo/50', 'GET', 'foo.buz.baz'); |
||||
417 | expect($route->getHost()->getPattern())->toBe('foo.{domain}.baz'); |
||||
418 | expect($route->params)->toBe(['domain' => 'buz', 'var1' => '50']); |
||||
419 | |||||
420 | }); |
||||
421 | |||||
422 | it("supports constrained host variables", function() { |
||||
423 | |||||
424 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
425 | $r->get('foo/{var1:\d+}', ['host' => '{subdomain:foo}.{domain}.bar'], function() {}); |
||||
426 | |||||
427 | $route = $r->route('foo/25', 'GET', 'foo.biz.bar'); |
||||
428 | expect($route->params)->toBe([ 'subdomain' => 'foo', 'domain' => 'biz', 'var1' => '25']); |
||||
429 | |||||
430 | try { |
||||
431 | $route = $r->route('foo/bar', 'GET', 'foo.biz.bar'); |
||||
0 ignored issues
–
show
|
|||||
432 | } catch (RouteNotFoundException $e) { |
||||
433 | expect($e->getMessage())->toBe("No route found for `*:foo.biz.bar:GET:/foo/bar`."); |
||||
434 | } |
||||
435 | }); |
||||
436 | |||||
437 | it("supports absolute URL", function() { |
||||
438 | |||||
439 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
440 | $r->get('foo/{var1:\d+}', ['host' => 'foo.{domain}.bar'], function() {}); |
||||
441 | $r->get('foo/{var1:\d+}', ['host' => 'foo.{domain}.baz'], function() {}); |
||||
442 | |||||
443 | $route = $r->route('http://foo.biz.bar/foo/25', 'GET'); |
||||
444 | expect($route->getHost()->getPattern())->toBe('foo.{domain}.bar'); |
||||
445 | expect($route->params)->toBe(['domain' => 'biz', 'var1' => '25']); |
||||
446 | |||||
447 | $route = $r->route('http://foo.buz.baz/foo/50', 'GET'); |
||||
448 | expect($route->getHost()->getPattern())->toBe('foo.{domain}.baz'); |
||||
449 | expect($route->params)->toBe(['domain' => 'buz', 'var1' => '50']); |
||||
450 | |||||
451 | }); |
||||
452 | |||||
453 | it("supports RESTful routes", function() { |
||||
454 | |||||
455 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
456 | $r->get('foo/bar', function () {}); |
||||
457 | $r->head('foo/bar', function () {}); |
||||
458 | $r->post('foo/bar', function () {}); |
||||
459 | $r->put('foo/bar', function () {}); |
||||
460 | $r->patch('foo/bar', function () {}); |
||||
461 | $r->delete('foo/bar', function () {}); |
||||
462 | $r->options('foo/bar', function () {}); |
||||
463 | |||||
464 | $methods = ['OPTIONS', 'DELETE', 'PATCH', 'PUT', 'POST', 'HEAD', 'GET']; |
||||
465 | |||||
466 | $route = $r->route('foo/bar', 'GET'); |
||||
467 | expect($route->getMethods())->toBe($methods); |
||||
468 | |||||
469 | $route = $r->route('foo/bar', 'HEAD'); |
||||
470 | expect($route->getMethods())->toBe($methods); |
||||
471 | |||||
472 | $route = $r->route('foo/bar', 'POST'); |
||||
473 | expect($route->getMethods())->toBe($methods); |
||||
474 | |||||
475 | $route = $r->route('foo/bar', 'PUT'); |
||||
476 | expect($route->getMethods())->toBe($methods); |
||||
477 | |||||
478 | $route = $r->route('foo/bar', 'PATCH'); |
||||
479 | expect($route->getMethods())->toBe($methods); |
||||
480 | |||||
481 | $route = $r->route('foo/bar', 'DELETE'); |
||||
482 | expect($route->getMethods())->toBe($methods); |
||||
483 | |||||
484 | $route = $r->route('foo/bar', 'OPTIONS'); |
||||
485 | expect($route->getMethods())->toBe($methods); |
||||
486 | |||||
487 | }); |
||||
488 | |||||
489 | it("matches relationships based routes", function() { |
||||
490 | |||||
491 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
492 | $r->get('[/{relations:[^/]+/[^/:][^/]*}]*/comment[/{id:[^/:][^/]*}][/:{action}]', function () {}); |
||||
493 | |||||
494 | $route = $r->route('blog/1/post/22/comment/:show', 'GET'); |
||||
495 | expect($route->params)->toBe([ |
||||
496 | 'relations' => [ |
||||
497 | ['blog', '1'], |
||||
498 | ['post', '22'] |
||||
499 | ], |
||||
500 | 'id' => null, |
||||
501 | 'action' => 'show' |
||||
502 | ]); |
||||
503 | |||||
504 | }); |
||||
505 | |||||
506 | it("dispatches HEAD requests on matching GET routes if the HEAD routes are missing", function() { |
||||
507 | |||||
508 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
509 | $r->get('foo/bar', function () { return 'GET'; }); |
||||
510 | |||||
511 | $route = $r->route('foo/bar', 'HEAD'); |
||||
512 | expect($route->getMethods())->toBe(['GET']); |
||||
513 | |||||
514 | }); |
||||
515 | |||||
516 | it("dispatches HEAD requests on matching HEAD routes", function() { |
||||
517 | |||||
518 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
519 | |||||
520 | $r->head('foo/bar', function () { return 'HEAD'; }); |
||||
521 | $r->get('foo/bar', function () { return 'GET'; }); |
||||
522 | |||||
523 | $route = $r->route('foo/bar', 'HEAD'); |
||||
524 | expect($route->getMethods())->toBe(['GET', 'HEAD']); |
||||
525 | |||||
526 | }); |
||||
527 | |||||
528 | it("supports requests as a list of arguments", function() { |
||||
529 | |||||
530 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
531 | $r->bind('foo/bar', function () {}); |
||||
532 | |||||
533 | $route = $r->route('foo/bar', 'GET'); |
||||
534 | expect($this->export($route->request))->toEqual([ |
||||
535 | 'scheme' => '*', |
||||
536 | 'host' => '*', |
||||
537 | 'method' => 'GET', |
||||
538 | 'path' => 'foo/bar' |
||||
539 | ]); |
||||
540 | |||||
541 | }); |
||||
542 | |||||
543 | it("supports requests as an object", function() { |
||||
544 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
545 | $r->bind('foo/bar', function () {}); |
||||
546 | |||||
547 | $request = Double::instance(['implements' => ServerRequestInterface::class]); |
||||
548 | $uri = Double::instance(['implements' => UriInterface::class]); |
||||
549 | |||||
550 | allow($request)->toReceive('basePath')->andReturn('/'); |
||||
551 | allow($uri)->toReceive('getScheme')->andReturn('http'); |
||||
552 | allow($uri)->toReceive('getHost')->andReturn(''); |
||||
553 | allow($uri)->toReceive('getPath')->andReturn('foo/bar'); |
||||
554 | allow($request)->toReceive('getMethod')->andReturn('GET'); |
||||
555 | allow($request)->toReceive('getUri')->andReturn($uri); |
||||
556 | |||||
557 | $route = $r->route($request, 'GET'); |
||||
558 | expect($route->request)->toBe($request); |
||||
559 | |||||
560 | }); |
||||
561 | |||||
562 | it("supports requests as an array", function() { |
||||
563 | |||||
564 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
565 | $r->bind('foo/bar', function () {}); |
||||
566 | |||||
567 | $route = $r->route(['path' =>'foo/bar'], 'GET'); |
||||
568 | expect($this->export($route->request))->toEqual([ |
||||
569 | 'scheme' => '*', |
||||
570 | 'host' => '*', |
||||
571 | 'method' => 'GET', |
||||
572 | 'path' => 'foo/bar' |
||||
573 | ]); |
||||
574 | |||||
575 | }); |
||||
576 | |||||
577 | }); |
||||
578 | |||||
579 | describe("->group()", function() { |
||||
580 | |||||
581 | it("supports nested named route", function() { |
||||
582 | |||||
583 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
584 | $r->group('foo', ['name' => 'foz'], function($r) use (&$route) { |
||||
585 | $r->group('bar', ['name' => 'baz'], function($r) use (&$route) { |
||||
586 | $route = $r->bind('{var1}', ['name' => 'quz'], function() {}); |
||||
587 | }); |
||||
588 | }); |
||||
589 | |||||
590 | expect(isset($r['foz.baz.quz']))->toBe(true); |
||||
591 | expect($r['foz.baz.quz'])->toBe($route); |
||||
592 | |||||
593 | }); |
||||
594 | |||||
595 | it("returns a working scope", function() { |
||||
596 | |||||
597 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
598 | $route = $r->group('foo', ['name' => 'foz'], function() { |
||||
599 | })->group('bar', ['name' => 'baz'], function() { |
||||
600 | })->bind('{var1}', ['name' => 'quz'], function() {}); |
||||
601 | |||||
602 | expect(isset($r['foz.baz.quz']))->toBe(true); |
||||
603 | expect($r['foz.baz.quz'])->toBe($route); |
||||
604 | |||||
605 | }); |
||||
606 | |||||
607 | context("with a prefix contraint", function() { |
||||
608 | |||||
609 | beforeEach(function() { |
||||
610 | |||||
611 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
612 | $r->group('foo', function($r) { |
||||
613 | $r->bind('{var1}', function () {}); |
||||
614 | }); |
||||
615 | |||||
616 | }); |
||||
617 | |||||
618 | it("respects url's prefix constraint", function() { |
||||
619 | |||||
620 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
621 | $route = $r->route('foo/bar'); |
||||
622 | expect($route->params)->toBe(['var1' => 'bar']); |
||||
623 | |||||
624 | }); |
||||
625 | |||||
626 | it("throws an exception when the prefix does not match", function () { |
||||
627 | |||||
628 | $closure = function () { |
||||
629 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
630 | $route = $r->route('bar/foo', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
631 | }; |
||||
632 | |||||
633 | expect($closure)->toThrow(new RouteNotFoundException("No route found for `*:*:GET:/bar/foo`.")); |
||||
634 | }); |
||||
635 | }); |
||||
636 | |||||
637 | context("with a prefix contraint and an optional parameter", function() { |
||||
638 | |||||
639 | beforeEach(function() { |
||||
640 | |||||
641 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
642 | $r->group('foo', function($r) { |
||||
643 | $r->bind('[{var1}]', function () {}); |
||||
644 | }); |
||||
645 | |||||
646 | }); |
||||
647 | |||||
648 | it("respects url's prefix constraint", function() { |
||||
649 | |||||
650 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
651 | $route = $r->route('foo'); |
||||
652 | expect($route->params)->toBe(['var1' => null]); |
||||
653 | |||||
654 | }); |
||||
655 | |||||
656 | it("throws an exception when the prefix does not match", function () { |
||||
657 | |||||
658 | $closure = function () { |
||||
659 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
660 | $route = $r->route('bar/foo', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
661 | }; |
||||
662 | |||||
663 | expect($closure)->toThrow(new RouteNotFoundException("No route found for `*:*:GET:/bar/foo`.")); |
||||
664 | }); |
||||
665 | }); |
||||
666 | |||||
667 | context("with a host constraint", function() { |
||||
668 | |||||
669 | beforeEach(function() { |
||||
670 | |||||
671 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
672 | $r->group('foo', ['host' => 'foo.{domain}.bar'], function($r) { |
||||
673 | $r->group('bar', function($r) { |
||||
674 | $r->bind('{var1}', function () {}); |
||||
675 | }); |
||||
676 | }); |
||||
677 | |||||
678 | }); |
||||
679 | |||||
680 | it("respects url's host constraint", function() { |
||||
681 | |||||
682 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
683 | $route = $r->route('http://foo.hello.bar/foo/bar/baz', 'GET'); |
||||
684 | expect($route->params)->toBe([ |
||||
685 | 'domain' => 'hello', |
||||
686 | 'var1' => 'baz' |
||||
687 | ]); |
||||
688 | |||||
689 | }); |
||||
690 | |||||
691 | it("throws an exception when the host does not match", function () { |
||||
692 | |||||
693 | $closure = function () { |
||||
694 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
695 | $route = $r->route('http://bar.hello.foo/foo/bar/baz', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
696 | }; |
||||
697 | |||||
698 | expect($closure)->toThrow(new RouteNotFoundException("No route found for `http:bar.hello.foo:GET:/foo/bar/baz`.")); |
||||
699 | }); |
||||
700 | }); |
||||
701 | |||||
702 | context("with a scheme constraint", function() { |
||||
703 | |||||
704 | beforeEach(function() { |
||||
705 | |||||
706 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
707 | $r->group('foo', ['scheme' => 'http'], function($r) { |
||||
708 | $r->group('bar', function($r) { |
||||
709 | $r->bind('{var1}', function () {}); |
||||
710 | }); |
||||
711 | }); |
||||
712 | |||||
713 | }); |
||||
714 | |||||
715 | it("respects url's scheme constraint", function() { |
||||
716 | |||||
717 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
718 | $route = $r->route('http://domain.com/foo/bar/baz', 'GET'); |
||||
719 | expect($route->params)->toBe([ |
||||
720 | 'var1' => 'baz' |
||||
721 | ]); |
||||
722 | |||||
723 | }); |
||||
724 | |||||
725 | it("throws an exception when route is not found", function () { |
||||
726 | |||||
727 | $closure = function () { |
||||
728 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
729 | $route = $r->route('https://domain.com/foo/bar/baz', 'GET'); |
||||
0 ignored issues
–
show
|
|||||
730 | }; |
||||
731 | |||||
732 | expect($closure)->toThrow(new RouteNotFoundException("No route found for `https:domain.com:GET:/foo/bar/baz`.")); |
||||
733 | }); |
||||
734 | }); |
||||
735 | |||||
736 | it("concats namespace values", function() { |
||||
737 | |||||
738 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
739 | $r->group('foo', ['namespace' => 'My'], function($r) { |
||||
740 | $r->group('bar', ['namespace' => 'Name'], function($r) { |
||||
741 | $r->bind('{var1}', ['namespace' => 'Space'], function () {}); |
||||
742 | }); |
||||
743 | }); |
||||
744 | |||||
745 | $route = $r->route('foo/bar/baz', 'GET'); |
||||
746 | expect($route->namespace)->toBe('My\Name\Space\\'); |
||||
747 | |||||
748 | }); |
||||
749 | |||||
750 | it("throws an exception when the handler is not a closure", function() { |
||||
751 | |||||
752 | $closure = function() { |
||||
753 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
754 | $r->group('foo', 'substr'); |
||||
755 | }; |
||||
756 | |||||
757 | expect($closure)->toThrow(new RouterException("The handler needs to be an instance of `Closure` or implements the `__invoke()` magic method.")); |
||||
758 | |||||
759 | }); |
||||
760 | |||||
761 | }); |
||||
762 | |||||
763 | describe("->middleware()", function() { |
||||
764 | |||||
765 | it("returns global middleware", function() { |
||||
766 | |||||
767 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
768 | |||||
769 | $mw1 = function($request, $response, $next) { |
||||
770 | return '1' . $next() . '1'; |
||||
771 | }; |
||||
772 | $mw2 = function($request, $response, $next) { |
||||
773 | return '2' . $next() . '2'; |
||||
774 | }; |
||||
775 | |||||
776 | $r->apply($mw1, $mw2); |
||||
777 | |||||
778 | $next = function() { return ''; }; |
||||
779 | |||||
780 | $generator = $r->middleware(); |
||||
781 | |||||
782 | $actual2 = $generator->current(); |
||||
783 | $generator->next(); |
||||
784 | $actual1 = $generator->current(); |
||||
785 | |||||
786 | expect($actual2(null, null, $next))->toBe('22'); |
||||
787 | expect($actual1(null, null, $next))->toBe('11'); |
||||
788 | |||||
789 | }); |
||||
790 | |||||
791 | }); |
||||
792 | |||||
793 | describe("->apply()", function() { |
||||
794 | |||||
795 | it("applies middlewares globally", function() { |
||||
796 | |||||
797 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
798 | |||||
799 | $r->apply(function($request, $response, $next) { |
||||
800 | return '1' . $next() . '1'; |
||||
801 | })->apply(function($request, $response, $next) { |
||||
802 | return '2' . $next() . '2'; |
||||
803 | }); |
||||
804 | |||||
805 | $route = $r->bind('/foo/bar', function($route) { |
||||
0 ignored issues
–
show
The parameter
$route is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
806 | return 'A'; |
||||
807 | }); |
||||
808 | |||||
809 | $route = $r->route('foo/bar'); |
||||
810 | $actual = $route->dispatch(); |
||||
811 | |||||
812 | expect($actual)->toBe('21A12'); |
||||
813 | |||||
814 | }); |
||||
815 | |||||
816 | it("applies middlewares globally and per groups", function() { |
||||
817 | |||||
818 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
819 | |||||
820 | $r->apply(function($request, $response, $next) { |
||||
821 | return '1' . $next() . '1'; |
||||
822 | }); |
||||
823 | |||||
824 | $r->bind('foo/{foo}', ['name' => 'foo'], function () { |
||||
825 | return 'A'; |
||||
826 | }); |
||||
827 | |||||
828 | $r->group('bar', function($r) { |
||||
829 | $r->bind('{bar}', ['name' => 'bar'], function () { |
||||
830 | return 'A'; |
||||
831 | }); |
||||
832 | })->apply(function($request, $response, $next) { |
||||
833 | return '2' . $next() . '2'; |
||||
834 | }); |
||||
835 | |||||
836 | $route = $r->route('foo/foo'); |
||||
837 | $actual = $route->dispatch(); |
||||
838 | |||||
839 | expect($actual)->toBe('1A1'); |
||||
840 | |||||
841 | $route = $r->route('bar/bar'); |
||||
842 | $actual = $route->dispatch(); |
||||
843 | |||||
844 | expect($actual)->toBe('21A12'); |
||||
845 | |||||
846 | }); |
||||
847 | |||||
848 | }); |
||||
849 | |||||
850 | describe("->strategy()", function() { |
||||
851 | |||||
852 | it("sets a strategy", function() { |
||||
853 | |||||
854 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
855 | |||||
856 | $mystrategy = function($router) { |
||||
857 | $router->bind('foo/bar', function() { |
||||
858 | return 'Hello World!'; |
||||
859 | }); |
||||
860 | }; |
||||
861 | |||||
862 | $r->strategy('mystrategy', $mystrategy); |
||||
863 | expect($r->strategy('mystrategy'))->toBe($mystrategy); |
||||
864 | |||||
865 | $r->mystrategy(); |
||||
866 | $route = $r->route('foo/bar'); |
||||
867 | expect($route->getPattern())->toBe('/foo/bar'); |
||||
868 | |||||
869 | }); |
||||
870 | |||||
871 | it("unsets a strategy", function() { |
||||
872 | |||||
873 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
874 | |||||
875 | $mystrategy = function() {}; |
||||
876 | |||||
877 | $r->strategy('mystrategy', $mystrategy); |
||||
878 | expect($r->strategy('mystrategy'))->toBe($mystrategy); |
||||
879 | |||||
880 | $r->strategy('mystrategy', false); |
||||
881 | expect($r->strategy('mystrategy'))->toBe(null); |
||||
882 | |||||
883 | }); |
||||
884 | |||||
885 | it("throws an exception when the handler is not a closure", function() { |
||||
886 | |||||
887 | $closure = function() { |
||||
888 | $r = $this->router; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
889 | $r->strategy('mystrategy', "substr"); |
||||
890 | }; |
||||
891 | |||||
892 | expect($closure)->toThrow(new RouterException("The handler needs to be an instance of `Closure` or implements the `__invoke()` magic method.")); |
||||
893 | |||||
894 | }); |
||||
895 | |||||
896 | }); |
||||
897 | |||||
898 | }); |
||||
899 |