feat: Page Editor v1.0 - canonical schema, SSR parity, and migration
Major improvements to WooNooW Page Editor system: Schema & Architecture: - Canonical section schema with unified sectionSchema.ts - Normalized feature-grid to use items (not features) - Standardized default values across all section types - Schema versioning with automatic migration on read Backend (PHP): - Enhanced PlaceholderRenderer with typed output contracts - Added fallback behavior for empty/invalid dynamic sources - Added caching support for post data resolution - New SchemaMigration class for backward compatibility - New Features class for feature flags - Enhanced PageSSR with full style support - Removed controller-level special-casing for related_posts Frontend (Admin SPA): - Updated CanvasRenderer with schema-aware transformation - Enhanced InspectorPanel with canonical schema metadata - Added new section renderers Frontend (Customer SPA): - New section components: BentoCategoryGrid, MarqueeBanner, ProductCarousel, ShoppableImage - Updated FeatureGridSection for items prop contract Testing: - Add PHP tests: SchemaMigrationTest, PlaceholderRendererTest, PageSSRTest - Add TypeScript tests: schema-integration, feature-grid-regression - Add parity tests for React vs SSR content matching - Add CI script: check-schema-drift.mjs - Add VERIFICATION_CHECKLIST.md Documentation: - RELEASE_NOTES-v1.0.md with full release notes - docs/PAGE_EDITOR_SECTION_SCHEMA_V1.md - docs/PAGE_EDITOR_SSR_COVERAGE_AUDIT.md
This commit is contained in:
207
tests/SchemaMigrationTest.php
Normal file
207
tests/SchemaMigrationTest.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
* Schema Migration Tests
|
||||
* Tests for backward compatibility and migration of legacy structures
|
||||
*/
|
||||
|
||||
namespace WooNooW\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use WooNooW\Frontend\SchemaMigration;
|
||||
|
||||
class SchemaMigrationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test that v1 structures don't need migration
|
||||
*/
|
||||
public function test_v1_structures_dont_need_migration()
|
||||
{
|
||||
$structure = [
|
||||
'type' => 'page',
|
||||
'schemaVersion' => 1,
|
||||
'sections' => [],
|
||||
];
|
||||
|
||||
$this->assertFalse(SchemaMigration::needs_migration($structure));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that structures without version need migration
|
||||
*/
|
||||
public function test_structures_without_version_need_migration()
|
||||
{
|
||||
$structure = [
|
||||
'type' => 'page',
|
||||
'sections' => [],
|
||||
];
|
||||
|
||||
$this->assertTrue(SchemaMigration::needs_migration($structure));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test migration of feature-grid with legacy features key
|
||||
*/
|
||||
public function test_migrate_feature_grid_features_to_items()
|
||||
{
|
||||
$structure = [
|
||||
'schemaVersion' => 0,
|
||||
'sections' => [
|
||||
[
|
||||
'type' => 'feature-grid',
|
||||
'props' => [
|
||||
'heading' => ['type' => 'static', 'value' => 'Test'],
|
||||
'features' => [
|
||||
['title' => 'Feature 1', 'description' => 'Desc 1'],
|
||||
['title' => 'Feature 2', 'description' => 'Desc 2'],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$migrated = SchemaMigration::migrate($structure);
|
||||
|
||||
$this->assertEquals(1, $migrated['schemaVersion']);
|
||||
$this->assertArrayHasKey('items', $migrated['sections'][0]['props']);
|
||||
$this->assertEquals($structure['sections'][0]['props']['features'], $migrated['sections'][0]['props']['items']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test migration of feature-grid with empty features string
|
||||
*/
|
||||
public function test_migrate_feature_grid_empty_features_to_items()
|
||||
{
|
||||
$structure = [
|
||||
'schemaVersion' => 0,
|
||||
'sections' => [
|
||||
[
|
||||
'type' => 'feature-grid',
|
||||
'props' => [
|
||||
'heading' => ['type' => 'static', 'value' => 'Test'],
|
||||
'features' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$migrated = SchemaMigration::migrate($structure);
|
||||
|
||||
$this->assertEquals([], $migrated['sections'][0]['props']['items']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test migration of container_width to contentWidth
|
||||
*/
|
||||
public function test_migrate_container_width_to_content_width()
|
||||
{
|
||||
$structure = [
|
||||
'schemaVersion' => 0,
|
||||
'sections' => [
|
||||
[
|
||||
'type' => 'hero',
|
||||
'styles' => [
|
||||
'container_width' => 'full',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$migrated = SchemaMigration::migrate($structure);
|
||||
|
||||
$this->assertArrayHasKey('contentWidth', $migrated['sections'][0]['styles']);
|
||||
$this->assertEquals('full', $migrated['sections'][0]['styles']['contentWidth']);
|
||||
$this->assertArrayNotHasKey('container_width', $migrated['sections'][0]['styles']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test migration of background image without type
|
||||
*/
|
||||
public function test_migrate_background_image_adds_type()
|
||||
{
|
||||
$structure = [
|
||||
'schemaVersion' => 0,
|
||||
'sections' => [
|
||||
[
|
||||
'type' => 'hero',
|
||||
'styles' => [
|
||||
'backgroundImage' => 'https://example.com/image.jpg',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$migrated = SchemaMigration::migrate($structure);
|
||||
|
||||
$this->assertEquals('image', $migrated['sections'][0]['styles']['backgroundType']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test migration of height to heightPreset
|
||||
*/
|
||||
public function test_migrate_height_to_height_preset()
|
||||
{
|
||||
$structure = [
|
||||
'schemaVersion' => 0,
|
||||
'sections' => [
|
||||
[
|
||||
'type' => 'hero',
|
||||
'styles' => [
|
||||
'height' => 'screen',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$migrated = SchemaMigration::migrate($structure);
|
||||
|
||||
$this->assertArrayHasKey('heightPreset', $migrated['sections'][0]['styles']);
|
||||
$this->assertEquals('fullscreen', $migrated['sections'][0]['styles']['heightPreset']);
|
||||
$this->assertArrayNotHasKey('height', $migrated['sections'][0]['styles']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test migration adds default backgroundType
|
||||
*/
|
||||
public function test_migrate_adds_default_background_type()
|
||||
{
|
||||
$structure = [
|
||||
'schemaVersion' => 0,
|
||||
'sections' => [
|
||||
[
|
||||
'type' => 'hero',
|
||||
'styles' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$migrated = SchemaMigration::migrate($structure);
|
||||
|
||||
$this->assertEquals('solid', $migrated['sections'][0]['styles']['backgroundType']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test batch migration
|
||||
*/
|
||||
public function test_migrate_all()
|
||||
{
|
||||
$structures = [
|
||||
['schemaVersion' => 0, 'sections' => []],
|
||||
['schemaVersion' => 1, 'sections' => []],
|
||||
['schemaVersion' => 0, 'sections' => []],
|
||||
];
|
||||
|
||||
$migrated = SchemaMigration::migrate_all($structures);
|
||||
|
||||
$this->assertEquals(1, $migrated[0]['schemaVersion']);
|
||||
$this->assertEquals(1, $migrated[1]['schemaVersion']);
|
||||
$this->assertEquals(1, $migrated[2]['schemaVersion']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test current version is returned
|
||||
*/
|
||||
public function test_get_current_version()
|
||||
{
|
||||
$this->assertEquals(1, SchemaMigration::get_current_version());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user