Skip to content

Commit 2668577

Browse files
committed
Add PHPUnit test cases for merge behavior
1 parent ca4ad98 commit 2668577

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

extra/html-extra/Tests/HtmlAttrMergeTest.php

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@
1212
namespace Twig\Extra\Html\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Twig\Error\RuntimeError;
16+
use Twig\Extra\Html\HtmlAttr\AttributeValueInterface;
17+
use Twig\Extra\Html\HtmlAttr\MergeableInterface;
1518
use Twig\Extra\Html\HtmlExtension;
1619

1720
class HtmlAttrMergeTest extends TestCase
1821
{
1922
/**
2023
* @dataProvider htmlAttrProvider
2124
*/
22-
public function testMerge(array $expected, array $inputs)
25+
public function testMerge(array $expected, array $inputs): void
2326
{
2427
$result = HtmlExtension::htmlAttrMerge(...$inputs);
2528

@@ -126,5 +129,94 @@ public static function htmlAttrProvider(): \Generator
126129
['style' => ['color' => 'red']],
127130
],
128131
];
132+
133+
// MergeableInterface
134+
yield 'MergeableInterface mergeInto is called when new value implements interface' => [
135+
['class' => 'merged: old + new'],
136+
[
137+
['class' => 'old'],
138+
['class' => new MergeableStub('new')],
139+
],
140+
];
141+
142+
yield 'MergeableInterface appendFrom is called when existing value implements interface' => [
143+
['class' => 'appended: old + new'],
144+
[
145+
['class' => new MergeableStub('old')],
146+
['class' => 'new'],
147+
],
148+
];
149+
150+
yield 'MergeableInterface mergeInto is called when both implement interface' => [
151+
['class' => 'merged: value1 + value2'],
152+
[
153+
['class' => new MergeableStub('value1')],
154+
['class' => new MergeableStub('value2')],
155+
],
156+
];
157+
158+
yield 'MergeableInterface with array value' => [
159+
['class' => 'appended: base + extra1, extra2'],
160+
[
161+
['class' => new MergeableStub('base')],
162+
['class' => ['extra1', 'extra2']],
163+
],
164+
];
165+
166+
// Scalar and object merging
167+
yield 'string replaces object' => [
168+
['value' => 'new-string'],
169+
[
170+
['value' => new \stdClass()],
171+
['value' => 'new-string'],
172+
],
173+
];
174+
175+
yield 'object replaces string' => [
176+
['value' => new \stdClass()],
177+
[
178+
['value' => 'old-string'],
179+
['value' => new \stdClass()],
180+
],
181+
];
182+
}
183+
184+
public function testIncompatibleValuesMergeThrowsException(): void
185+
{
186+
$this->expectException(RuntimeError::class);
187+
$this->expectExceptionMessage('Cannot merge incompatible values for key "test"');
188+
189+
HtmlExtension::htmlAttrMerge(
190+
['test' => ['array']],
191+
['test' => 'scalar']
192+
);
193+
}
194+
}
195+
196+
class MergeableStub implements MergeableInterface
197+
{
198+
public function __construct(private readonly mixed $value)
199+
{
200+
}
201+
202+
public function mergeInto(mixed $previous): self
203+
{
204+
$previousValue = $previous instanceof self ? $previous->value : $previous;
205+
return new self("merged: {$previousValue} + {$this->value}");
206+
}
207+
208+
public function appendFrom(mixed $newValue): self
209+
{
210+
if (is_array($newValue)) {
211+
$newValue = implode(', ', $newValue);
212+
} elseif ($newValue instanceof self) {
213+
$newValue = $newValue->value;
214+
}
215+
return new self("appended: {$this->value} + {$newValue}");
216+
}
217+
218+
public function __toString(): string
219+
{
220+
return (string) $this->value;
129221
}
130222
}

extra/html-extra/Tests/HtmlAttrTest.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class HtmlAttrTest extends TestCase
2222
/**
2323
* @dataProvider htmlAttrProvider
2424
*/
25-
public function testPrintingAttributes(string $expected, array $inputs)
25+
public function testPrintingAttributes(string $expected, array $inputs): void
2626
{
2727
$result = HtmlExtension::htmlAttr(new Environment(new ArrayLoader()), ...$inputs);
2828

@@ -228,6 +228,35 @@ public static function htmlAttrProvider(): \Generator
228228
['data-count' => 0],
229229
],
230230
];
231+
232+
// Scalar and object merging in rendering
233+
yield 'string replaces object in rendering' => [
234+
'value="new-string"',
235+
[
236+
['value' => new \stdClass()],
237+
['value' => 'new-string'],
238+
],
239+
];
240+
241+
yield 'object replaces string in rendering uses __toString if available' => [
242+
'value="stringable-object"',
243+
[
244+
['value' => 'old-string'],
245+
['value' => new StringableStub('stringable-object')],
246+
],
247+
];
248+
}
249+
}
250+
251+
class StringableStub implements \Stringable
252+
{
253+
public function __construct(private readonly string $value)
254+
{
255+
}
256+
257+
public function __toString(): string
258+
{
259+
return $this->value;
231260
}
232261
}
233262

0 commit comments

Comments
 (0)