Skip to content

Commit c668c30

Browse files
committed
LiveUrlSubscriberTest
1 parent d005f12 commit c668c30

File tree

2 files changed

+228
-14
lines changed

2 files changed

+228
-14
lines changed

src/LiveComponent/src/EventListener/LiveUrlSubscriber.php

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
1919
use Symfony\UX\LiveComponent\Util\UrlFactory;
2020

21+
/**
22+
* @internal
23+
*/
2124
class LiveUrlSubscriber implements EventSubscriberInterface
2225
{
2326
private const URL_HEADER = 'X-Live-Url';
@@ -41,13 +44,11 @@ public function onKernelResponse(ResponseEvent $event): void
4144
$newUrl = null;
4245
if ($previousLocation = $request->headers->get(self::URL_HEADER)) {
4346
$liveProps = $this->getLivePropsToMap($request);
44-
if (!empty($liveProps)) {
45-
$newUrl = $this->urlFactory->createFromPreviousAndProps(
46-
$previousLocation,
47-
$liveProps['path'],
48-
$liveProps['query']
49-
);
50-
}
47+
$newUrl = $this->urlFactory->createFromPreviousAndProps(
48+
$previousLocation,
49+
$liveProps['path'],
50+
$liveProps['query']
51+
);
5152
}
5253

5354
if ($newUrl) {
@@ -71,20 +72,20 @@ private function getLivePropsToMap(Request $request): array
7172
$component = $request->attributes->get('_mounted_component');
7273
$metadata = $this->metadataFactory->getMetadata($componentName);
7374

74-
$liveData = $request->attributes->get('_live_request_data') ?? [];
75+
$liveRequestData = $request->attributes->get('_live_request_data') ?? [];
7576
$values = array_merge(
76-
$liveData['props'] ?? [],
77-
$liveData['updated'] ?? [],
78-
$liveData['responseProps'] ?? []
77+
$liveRequestData['props'] ?? [],
78+
$liveRequestData['updated'] ?? [],
79+
$liveRequestData['responseProps'] ?? []
7980
);
8081

8182
$urlLiveProps = [
8283
'path' => [],
8384
'query' => [],
8485
];
85-
foreach ($metadata->getAllLivePropsMetadata($component) as $liveProp) {
86-
$name = $liveProp->getName();
87-
$urlMapping = $liveProp->urlMapping();
86+
foreach ($metadata->getAllLivePropsMetadata($component) as $livePropMetadata) {
87+
$name = $livePropMetadata->getName();
88+
$urlMapping = $livePropMetadata->urlMapping();
8889
if (isset($values[$name]) && $urlMapping) {
8990
$urlLiveProps[$urlMapping->mapPath ? 'path' : 'query'][$urlMapping->as ?? $name] =
9091
$values[$name];
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\LiveComponent\Tests\Unit\EventListener;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\HttpKernel\Event\ResponseEvent;
18+
use Symfony\Component\HttpKernel\HttpKernelInterface;
19+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
20+
use Symfony\UX\LiveComponent\EventListener\LiveUrlSubscriber;
21+
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadata;
22+
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
23+
use Symfony\UX\LiveComponent\Metadata\LivePropMetadata;
24+
use Symfony\UX\LiveComponent\Metadata\UrlMapping;
25+
use Symfony\UX\LiveComponent\Util\UrlFactory;
26+
27+
class LiveUrlSubscriberTest extends TestCase
28+
{
29+
public function getIgnoreData(): iterable
30+
{
31+
yield 'not_a_live_component' => [
32+
'attributes' => [],
33+
'requestType' => HttpKernelInterface::MAIN_REQUEST,
34+
'headers' => ['X-Live-Url' => '/foo/bar'],
35+
];
36+
yield 'not_main_request' => [
37+
'attributes' => ['_live_component' => 'componentName'],
38+
'requestType' => HttpKernelInterface::SUB_REQUEST,
39+
'headers' => ['X-Live-Url' => '/foo/bar'],
40+
];
41+
yield 'no_previous_url' => [
42+
'attributes' => ['_live_component' => 'componentName'],
43+
'requestType' => HttpKernelInterface::MAIN_REQUEST,
44+
'headers' => [],
45+
];
46+
}
47+
48+
/**
49+
* @dataProvider getIgnoreData
50+
*/
51+
public function testDoNothing(
52+
array $attributes = ['_live_component' => 'componentName'],
53+
int $requestType = HttpKernelInterface::MAIN_REQUEST,
54+
array $headers = ['X-Live-Url' => '/foo/bar'],
55+
): void {
56+
$request = new Request();
57+
$request->attributes->add($attributes);
58+
$request->headers->add($headers);
59+
$response = new Response();
60+
$event = new ResponseEvent(
61+
$this->createMock(HttpKernelInterface::class),
62+
$request,
63+
$requestType,
64+
$response
65+
);
66+
67+
$metadataFactory = $this->createMock(LiveComponentMetadataFactory::class);
68+
$metadataFactory->expects(self::never())->method('getMetadata');
69+
$urlFactory = $this->createMock(UrlFactory::class);
70+
$urlFactory->expects(self::never())->method('createFromPreviousAndProps');
71+
$liveUrlSubscriber = new LiveUrlSubscriber($metadataFactory, $urlFactory);
72+
73+
$liveUrlSubscriber->onKernelResponse($event);
74+
$this->assertNull($response->headers->get('X-Live-Url'));
75+
}
76+
77+
public function getData(): iterable
78+
{
79+
yield 'prop_without_matching_property' => [
80+
'liveRequestData' => [
81+
'props' => ['notMatchingProp' => 0],
82+
],
83+
];
84+
yield 'prop_matching_non_mapped_property' => [
85+
'liveRequestData' => [
86+
'props' => ['nonMappedProp' => 0],
87+
],
88+
];
89+
yield 'props_matching_query_mapped_properties' => [
90+
'liveRequestData' => [
91+
'props' => ['queryMappedProp1' => 1],
92+
'updated' => ['queryMappedProp2' => 2],
93+
'responseProps' => ['queryMappedProp3' => 3],
94+
],
95+
'expectedPathProps' => [],
96+
'expectedQueryProps' => [
97+
'queryMappedProp1' => 1,
98+
'queryMappedProp2' => 2,
99+
'queryMappedProp3' => 3,
100+
],
101+
];
102+
yield 'props_matching_path_mapped_properties' => [
103+
'liveRequestData' => [
104+
'props' => ['pathMappedProp1' => 1],
105+
'updated' => ['pathMappedProp2' => 2],
106+
'responseProps' => ['pathMappedProp3' => 3],
107+
],
108+
'expectedPathProps' => [
109+
'pathMappedProp1' => 1,
110+
'pathMappedProp2' => 2,
111+
'pathMappedProp3' => 3,
112+
],
113+
'expectedQueryProps' => [],
114+
];
115+
yield 'props_matching_properties_with_alias' => [
116+
'liveRequestData' => [
117+
'props' => ['pathMappedPropWithAlias' => 1, 'queryMappedPropWithAlias' => 2],
118+
],
119+
'expectedPathProps' => ['pathAlias' => 1],
120+
'expectedQueryProps' => ['queryAlias' => 2],
121+
];
122+
yield 'responseProps_have_highest_priority' => [
123+
'liveRequestData' => [
124+
'props' => ['queryMappedProp1' => 1],
125+
'updated' => ['queryMappedProp1' => 2],
126+
'responseProps' => ['queryMappedProp1' => 3],
127+
],
128+
'expectedPathProps' => [],
129+
'expectedQueryProps' => ['queryMappedProp1' => 3],
130+
];
131+
yield 'updated_have_second_priority' => [
132+
'liveRequestData' => [
133+
'props' => ['queryMappedProp1' => 1],
134+
'updated' => ['queryMappedProp1' => 2],
135+
],
136+
'expectedPathProps' => [],
137+
'expectedQueryProps' => ['queryMappedProp1' => 2],
138+
];
139+
}
140+
141+
/**
142+
* @dataProvider getData
143+
*/
144+
public function testProps(
145+
array $liveRequestData,
146+
array $expectedPathProps = [],
147+
array $expectedQueryProps = []
148+
): void {
149+
$previousLocation = '/foo/bar';
150+
$newLocation = '/foo/baz';
151+
$componentName = 'componentName';
152+
$component = $this->createMock(\stdClass::class);
153+
$metaData = $this->createMock(LiveComponentMetadata::class);
154+
$metaData->expects(self::once())
155+
->method('getAllLivePropsMetadata')
156+
->with($component)
157+
->willReturn([
158+
$this->createLivePropMetadata('nonMappedProp'),
159+
$this->createLivePropMetadata('queryMappedProp1', true),
160+
$this->createLivePropMetadata('queryMappedProp2', true),
161+
$this->createLivePropMetadata('queryMappedProp3', true),
162+
$this->createLivePropMetadata('pathMappedProp1', true, true),
163+
$this->createLivePropMetadata('pathMappedProp2', true, true),
164+
$this->createLivePropMetadata('pathMappedProp3', true, true),
165+
$this->createLivePropMetadata('queryMappedPropWithAlias', true, false, 'queryAlias'),
166+
$this->createLivePropMetadata('pathMappedPropWithAlias', true, true, 'pathAlias'),
167+
]);
168+
$request = new Request();
169+
$request->attributes->add([
170+
'_live_component' => $componentName,
171+
'_mounted_component' => $component,
172+
'_live_request_data' => $liveRequestData,
173+
]);
174+
$request->headers->add(['X-Live-Url' => $previousLocation]);
175+
$response = new Response();
176+
$event = new ResponseEvent(
177+
$this->createMock(HttpKernelInterface::class),
178+
$request,
179+
HttpKernelInterface::MAIN_REQUEST,
180+
$response
181+
);
182+
183+
$metadataFactory = $this->createMock(LiveComponentMetadataFactory::class);
184+
$metadataFactory->expects(self::once())->method('getMetadata')->with($componentName)->willReturn($metaData);
185+
$urlFactory = $this->createMock(UrlFactory::class);
186+
$liveUrlSubscriber = new LiveUrlSubscriber($metadataFactory, $urlFactory);
187+
188+
$urlFactory->expects(self::once())
189+
->method('createFromPreviousAndProps')
190+
->with($previousLocation, $expectedPathProps, $expectedQueryProps)
191+
->willReturn($newLocation);
192+
$liveUrlSubscriber->onKernelResponse($event);
193+
$this->assertEquals($newLocation, $response->headers->get('X-Live-Url'));
194+
}
195+
196+
private function createLivePropMetadata(
197+
string $name,
198+
bool $url = false,
199+
bool $mapPath = false,
200+
?string $as = null,
201+
): LivePropMetadata {
202+
return new LivePropMetadata(
203+
$name,
204+
new LiveProp(
205+
url: $url ? new UrlMapping($as, $mapPath) : false
206+
),
207+
null,
208+
true,
209+
true,
210+
null
211+
);
212+
}
213+
}

0 commit comments

Comments
 (0)