| 1 |
<?php |
| 2 |
|
| 3 |
namespace Extendify\Tests\Integration\QuickEdit\Schemas; |
| 4 |
|
| 5 |
use Extendify\QuickEdit\Schemas\Schema; |
| 6 |
use WP_UnitTestCase; |
| 7 |
|
| 8 |
class SchemaContractTest extends WP_UnitTestCase |
| 9 |
{ |
| 10 |
public function test_schema_is_an_interface() |
| 11 |
{ |
| 12 |
$reflection = new \ReflectionClass(Schema::class); |
| 13 |
|
| 14 |
$this->assertTrue($reflection->isInterface()); |
| 15 |
} |
| 16 |
|
| 17 |
public function test_interface_declares_only_fields_and_apply() |
| 18 |
{ |
| 19 |
$reflection = new \ReflectionClass(Schema::class); |
| 20 |
$names = array_map( |
| 21 |
static fn (\ReflectionMethod $m) => $m->getName(), |
| 22 |
$reflection->getMethods() |
| 23 |
); |
| 24 |
sort($names); |
| 25 |
|
| 26 |
// The interface intentionally has no load/validate/error hooks — |
| 27 |
// save orchestration lives in SaveController, not the schema. |
| 28 |
$this->assertSame(['apply', 'fields'], $names); |
| 29 |
} |
| 30 |
|
| 31 |
public function test_apply_signature_takes_block_fieldKey_value_and_returns_array() |
| 32 |
{ |
| 33 |
$reflection = new \ReflectionMethod(Schema::class, 'apply'); |
| 34 |
$params = $reflection->getParameters(); |
| 35 |
|
| 36 |
$this->assertCount(3, $params); |
| 37 |
$this->assertSame('block', $params[0]->getName()); |
| 38 |
$this->assertSame('fieldKey', $params[1]->getName()); |
| 39 |
$this->assertSame('value', $params[2]->getName()); |
| 40 |
|
| 41 |
$this->assertSame('array', (string) $params[0]->getType()); |
| 42 |
$this->assertSame('string', (string) $params[1]->getType()); |
| 43 |
$this->assertFalse($params[2]->hasType(), 'value is intentionally untyped (mixed payload)'); |
| 44 |
|
| 45 |
$this->assertSame('array', (string) $reflection->getReturnType()); |
| 46 |
} |
| 47 |
|
| 48 |
public function test_fields_signature_returns_array() |
| 49 |
{ |
| 50 |
$reflection = new \ReflectionMethod(Schema::class, 'fields'); |
| 51 |
|
| 52 |
$this->assertCount(0, $reflection->getParameters()); |
| 53 |
$this->assertSame('array', (string) $reflection->getReturnType()); |
| 54 |
} |
| 55 |
|
| 56 |
public function test_a_concrete_schema_round_trips_through_apply() |
| 57 |
{ |
| 58 |
$schema = new FakeSchema(); |
| 59 |
|
| 60 |
$this->assertSame( |
| 61 |
[ |
| 62 |
['key' => 'title', 'control' => 'text', 'label' => 'Title'], |
| 63 |
], |
| 64 |
$schema->fields() |
| 65 |
); |
| 66 |
|
| 67 |
$block = ['blockName' => 'fake/block', 'attrs' => [], 'innerHTML' => 'old']; |
| 68 |
|
| 69 |
// Known field mutates. |
| 70 |
$mutated = $schema->apply($block, 'title', 'new'); |
| 71 |
$this->assertSame('new', $mutated['attrs']['title']); |
| 72 |
|
| 73 |
// Unknown field returns the block unchanged — characterizes the |
| 74 |
// pattern every concrete schema follows (default-return branch). |
| 75 |
$this->assertSame($block, $schema->apply($block, 'unknown-field', 'whatever')); |
| 76 |
} |
| 77 |
} |
| 78 |
|
| 79 |
class FakeSchema implements Schema |
| 80 |
{ |
| 81 |
public function fields(): array |
| 82 |
{ |
| 83 |
return [ |
| 84 |
['key' => 'title', 'control' => 'text', 'label' => 'Title'], |
| 85 |
]; |
| 86 |
} |
| 87 |
|
| 88 |
public function apply(array $block, string $fieldKey, $value): array |
| 89 |
{ |
| 90 |
if ($fieldKey !== 'title') { |
| 91 |
return $block; |
| 92 |
} |
| 93 |
$attrs = $block['attrs'] ?? []; |
| 94 |
$attrs['title'] = (string) $value; |
| 95 |
$block['attrs'] = $attrs; |
| 96 |
return $block; |
| 97 |
} |
| 98 |
} |
| 99 |
|