diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 9483675..e2df321 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -68,6 +68,12 @@ jobs: repository: TangibleInc/database-module path: database-module + - name: Checkout fields + uses: actions/checkout@v6 + with: + repository: TangibleInc/fields + path: fields + - name: Setup WordPress test config run: | cp wordpress-develop/wp-tests-config-sample.php wordpress-develop/wp-tests-config.php @@ -80,4 +86,5 @@ jobs: env: WORDPRESS_DEVELOP_DIR: ${{ github.workspace }}/wordpress-develop DATABASE_MODULE_DIR: ${{ github.workspace }}/database-module + TANGIBLE_FIELDS_DIR: ${{ github.workspace }}/fields run: ./vendor/bin/phpunit diff --git a/Makefile b/Makefile index 103d1fc..72c9e76 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ # Default WordPress develop location WORDPRESS_DEVELOP_DIR ?= $(HOME)/workspace/wordpress-develop DATABASE_MODULE_DIR ?= $(shell dirname $(CURDIR))/database-module +TANGIBLE_FIELDS_DIR ?= $(shell dirname $(CURDIR))/fields help: @echo "Available targets:" @@ -22,9 +23,10 @@ test: test-all: setup-integrations WORDPRESS_DEVELOP_DIR=$(WORDPRESS_DEVELOP_DIR) \ DATABASE_MODULE_DIR=$(DATABASE_MODULE_DIR) \ + TANGIBLE_FIELDS_DIR=$(TANGIBLE_FIELDS_DIR) \ ./vendor/bin/phpunit -setup-integrations: setup-database-module +setup-integrations: setup-database-module setup-tangible-fields setup-database-module: @if [ ! -d "$(DATABASE_MODULE_DIR)" ]; then \ @@ -34,6 +36,14 @@ setup-database-module: echo "database-module already exists at $(DATABASE_MODULE_DIR)"; \ fi +setup-tangible-fields: + @if [ ! -d "$(TANGIBLE_FIELDS_DIR)" ]; then \ + echo "Cloning tangible-fields to $(TANGIBLE_FIELDS_DIR)..."; \ + git clone https://github.com/tangibleinc/fields.git $(TANGIBLE_FIELDS_DIR); \ + else \ + echo "tangible-fields already exists at $(TANGIBLE_FIELDS_DIR)"; \ + fi + setup-wp: @if [ ! -d "$(WORDPRESS_DEVELOP_DIR)" ]; then \ echo "Cloning wordpress-develop to $(WORDPRESS_DEVELOP_DIR)..."; \ diff --git a/composer.json b/composer.json index 1eacbdb..66be74c 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,16 @@ { "name": "tangible/object", "description": "A WordPress tool suite for building data-driven interfaces with a clean extensible architecture.", - "repositories": [], + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/TangibleInc/framework" + } + ], "require-dev": { "phpunit/phpunit": "^9.6.31", - "yoast/phpunit-polyfills": "^2.0.5" + "yoast/phpunit-polyfills": "^2.0.5", + "tangible/framework": "dev-main" }, "require": {}, "autoload": { @@ -20,9 +26,11 @@ "scripts": { "test": "phpunit", "setup:integrations": [ - "@setup:database-module" + "@setup:database-module", + "@setup:tangible-fields" ], - "setup:database-module": "[ -d ../database-module ] || git clone https://github.com/tangibleinc/database-module.git ../database-module" + "setup:database-module": "[ -d ../database-module ] || git clone https://github.com/tangibleinc/database-module.git ../database-module", + "setup:tangible-fields": "[ -d ../fields ] || git clone https://github.com/tangibleinc/fields.git ../fields" }, "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index 4943120..0740c34 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0477da069b1022bb253712240acec1d1", + "content-hash": "fd7a805b83330622729f37d9becaafc8", "packages": [], "packages-dev": [ { @@ -1754,6 +1754,32 @@ ], "time": "2020-09-28T06:39:44+00:00" }, + { + "name": "tangible/framework", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/TangibleInc/framework.git", + "reference": "03fc17b06183241af08efb990ec053d9acfc7bf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TangibleInc/framework/zipball/03fc17b06183241af08efb990ec053d9acfc7bf7", + "reference": "03fc17b06183241af08efb990ec053d9acfc7bf7", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "^9.6", + "yoast/phpunit-polyfills": "^2.0" + }, + "default-branch": true, + "type": "library", + "support": { + "source": "https://github.com/TangibleInc/framework/tree/latest", + "issues": "https://github.com/TangibleInc/framework/issues" + }, + "time": "2025-12-15T00:12:33+00:00" + }, { "name": "theseer/tokenizer", "version": "1.3.1", @@ -1870,10 +1896,12 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": {}, + "stability-flags": { + "tangible/framework": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": {}, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 48aef45..fdb29c8 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -30,6 +30,14 @@ $_DATABASE_MODULE_DIR = __DIR__ . '/../../database-module'; } +/** + * Optional: Load the Tangible Fields library for TangibleFieldsRenderer tests. + * Set TANGIBLE_FIELDS_DIR environment variable to override the default path. + */ +if ( ! $_TANGIBLE_FIELDS_DIR = getenv( 'TANGIBLE_FIELDS_DIR' ) ) { + $_TANGIBLE_FIELDS_DIR = __DIR__ . '/../../fields'; +} + // Load WP test functions first (provides tests_add_filter) require_once $_WORDPRESS_TESTS_DIR . '/includes/functions.php'; @@ -40,5 +48,17 @@ }); } +// Load tangible-fields if available (requires tangible-framework) +if ( file_exists( $_TANGIBLE_FIELDS_DIR . '/index.php' ) ) { + tests_add_filter( 'muplugins_loaded', function() use ( $_TANGIBLE_FIELDS_DIR ) { + // Load tangible-framework first (required by tangible-fields) + $framework_path = __DIR__ . '/../vendor/tangible/framework/index.php'; + if ( file_exists( $framework_path ) ) { + require_once $framework_path; + } + require_once $_TANGIBLE_FIELDS_DIR . '/index.php'; + }); +} + // Now load the rest of WordPress require $_WORDPRESS_TESTS_DIR . '/includes/bootstrap.php'; diff --git a/tests/phpunit/data-view.php b/tests/phpunit/data-view.php index ef0aaa3..15c4e68 100644 --- a/tests/phpunit/data-view.php +++ b/tests/phpunit/data-view.php @@ -1240,6 +1240,90 @@ public function test_tangible_fields_renderer_throws_without_framework(): void { $renderer->render_editor( $layout, [] ); } + /** + * ========================================================================== + * TangibleFieldsRenderer Integration Tests (require Tangible Fields framework) + * ========================================================================== + */ + + public function test_tangible_fields_framework_is_available(): void { + if ( ! function_exists( 'tangible_fields' ) ) { + $this->markTestSkipped( 'Tangible Fields framework is not loaded.' ); + } + + $fields = tangible_fields(); + $this->assertNotNull( $fields, 'tangible_fields() should return a Fields instance' ); + $this->assertIsObject( $fields ); + } + + public function test_tangible_fields_renderer_renders_list_view(): void { + if ( ! function_exists( 'tangible_fields' ) ) { + $this->markTestSkipped( 'Tangible Fields framework is not loaded.' ); + } + + $dataset = new DataSet(); + $dataset->add_string( 'name' ); + $dataset->add_integer( 'count' ); + + $renderer = new \Tangible\Renderer\TangibleFieldsRenderer(); + $html = $renderer->render_list( $dataset, [ + [ 'name' => 'Item 1', 'count' => 5 ], + [ 'name' => 'Item 2', 'count' => 10 ], + [ 'name' => 'Item 3', 'count' => 15 ], + ] ); + + $this->assertStringContainsString( 'wp-list-table', $html ); + $this->assertStringContainsString( 'Item 1', $html ); + $this->assertStringContainsString( 'Item 2', $html ); + $this->assertStringContainsString( 'Item 3', $html ); + } + + public function test_tangible_fields_renderer_list_view_handles_boolean(): void { + if ( ! function_exists( 'tangible_fields' ) ) { + $this->markTestSkipped( 'Tangible Fields framework is not loaded.' ); + } + + $dataset = new DataSet(); + $dataset->add_string( 'name' ); + $dataset->add_boolean( 'active' ); + + $renderer = new \Tangible\Renderer\TangibleFieldsRenderer(); + $html = $renderer->render_list( $dataset, [ + [ 'name' => 'Item 1', 'active' => true ], + [ 'name' => 'Item 2', 'active' => false ], + ] ); + + $this->assertStringContainsString( 'Yes', $html ); + $this->assertStringContainsString( 'No', $html ); + } + + public function test_tangible_fields_renderer_list_view_handles_repeater(): void { + if ( ! function_exists( 'tangible_fields' ) ) { + $this->markTestSkipped( 'Tangible Fields framework is not loaded.' ); + } + + $dataset = new DataSet(); + $dataset->add_string( 'name' ); + $dataset->add_string( 'items' ); + + $renderer = new \Tangible\Renderer\TangibleFieldsRenderer(); + $renderer->set_field_configs( [ + 'items' => [ + 'type' => 'repeater', + 'sub_fields' => [ + [ 'name' => 'title', 'type' => 'string' ], + ], + ], + ] ); + + $html = $renderer->render_list( $dataset, [ + [ 'name' => 'Test', 'items' => json_encode( [ [ 'title' => 'A' ], [ 'title' => 'B' ] ] ) ], + ] ); + + // Repeater fields should show item count in list view. + $this->assertStringContainsString( '2 item(s)', $html ); + } + /** * ========================================================================== * DataView with TangibleFieldsRenderer Tests