From 8d906adb483c3e769e8567767c5a13ee9a699a3a Mon Sep 17 00:00:00 2001 From: David Stone Date: Sat, 11 Apr 2026 22:03:35 -0600 Subject: [PATCH] test: add template_id checkout validation tests and fix Dashboard_Widgets_Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 7 unit tests covering the template_selection → template_id validation mapping fix from PR #800: - template_selection required attribute maps to template_id rule key - min:1 guard rejects template_id=0 during checkout - positive template_id passes validation - no min:1 added when template_selection field is absent - template_id=0 allowed with base rule (admin/network context) - non-template required fields still map to themselves Fix Dashboard_Widgets_Test failure caused by PR #785 adding is_network_admin() guard to enqueue_scripts(). The test now uses set_current_screen('dashboard-network') to simulate the network admin context. Also adds a new test confirming the per-site dashboard does NOT enqueue wu-activity-stream. --- tests/WP_Ultimo/Checkout/Checkout_Test.php | 192 +++++++++++++++++++++ tests/WP_Ultimo/Dashboard_Widgets_Test.php | 48 +++++- 2 files changed, 238 insertions(+), 2 deletions(-) diff --git a/tests/WP_Ultimo/Checkout/Checkout_Test.php b/tests/WP_Ultimo/Checkout/Checkout_Test.php index 9205a559..98d2b13a 100644 --- a/tests/WP_Ultimo/Checkout/Checkout_Test.php +++ b/tests/WP_Ultimo/Checkout/Checkout_Test.php @@ -4560,6 +4560,198 @@ public function test_get_checkout_variables_with_discount_code_in_order(): void unset($_REQUEST['discount_code']); } + // ------------------------------------------------------------------------- + // template_id validation — field_to_rule_key mapping (PR #800 fix) + // ------------------------------------------------------------------------- + + /** + * Test get_validation_rules maps template_selection required to template_id rule. + * + * The template_selection signup field has force_attributes() { required: true }, + * but its POST key is template_id (not template_selection). Before PR #800 + * the required rule was applied to the wrong key. + */ + public function test_get_validation_rules_maps_template_selection_required_to_template_id(): void { + + $checkout = Checkout::get_instance(); + $checkout->step = [ + 'fields' => [ + ['id' => 'template_selection', 'required' => true], + ], + ]; + $checkout->steps = []; + $checkout->step_name = null; + + unset($_REQUEST['pre-flight'], $_REQUEST['checkout_form']); + + $rules = $checkout->get_validation_rules(); + + // template_id rule must include 'required' (mapped from template_selection) + $this->assertArrayHasKey('template_id', $rules); + $this->assertStringContainsString('required', $rules['template_id']); + + // template_selection should NOT have its own rule entry + $this->assertArrayNotHasKey('template_selection', $rules); + } + + /** + * Test get_validation_rules adds min:1 to template_id when required. + * + * Rakit's required rule accepts integer 0 as "present", so min:1 is + * needed to reject template_id=0 during checkout. + */ + public function test_get_validation_rules_adds_min_1_to_template_id(): void { + + $checkout = Checkout::get_instance(); + $checkout->step = [ + 'fields' => [ + ['id' => 'template_selection', 'required' => true], + ], + ]; + $checkout->steps = []; + $checkout->step_name = null; + + unset($_REQUEST['pre-flight'], $_REQUEST['checkout_form']); + + $rules = $checkout->get_validation_rules(); + + $this->assertStringContainsString('min:1', $rules['template_id']); + } + + /** + * Test validate rejects template_id=0 when template_selection is required. + * + * This is the core regression test: a checkout with a required template + * selection field must not allow template_id=0 through validation. + */ + public function test_validate_rejects_template_id_zero_when_required(): void { + + $checkout = Checkout::get_instance(); + $checkout->step = [ + 'fields' => [ + ['id' => 'template_selection', 'required' => true], + ], + ]; + $checkout->steps = []; + $checkout->step_name = null; + + $this->ensure_session($checkout); + + $_REQUEST['template_id'] = 0; + + unset($_REQUEST['pre-flight'], $_REQUEST['checkout_form']); + + $rules = $checkout->get_validation_rules(); + $result = $checkout->validate($rules); + + $this->assertInstanceOf(\WP_Error::class, $result, 'template_id=0 should fail validation when template_selection is required'); + + unset($_REQUEST['template_id']); + } + + /** + * Test validate accepts a valid non-zero template_id when required. + * + * Uses min:1 rule directly to verify a positive integer passes. + */ + public function test_validate_accepts_positive_template_id(): void { + + $checkout = Checkout::get_instance(); + $checkout->step = ['fields' => []]; + $checkout->steps = []; + $checkout->step_name = null; + + $this->ensure_session($checkout); + + $_REQUEST['template_id'] = 5; + + // min:1 should pass for value 5 + $result = $checkout->validate(['template_id' => 'integer|min:1']); + + $this->assertTrue($result); + + unset($_REQUEST['template_id']); + } + + /** + * Test get_validation_rules does NOT add min:1 to template_id when + * template_selection is absent (no template step on the form). + * + * This ensures admin/API paths that don't include a template_selection + * field are not affected by the checkout-specific guard. + */ + public function test_get_validation_rules_no_min_1_without_template_selection_field(): void { + + $checkout = Checkout::get_instance(); + $checkout->step = [ + 'fields' => [ + ['id' => 'email_address', 'required' => true], + ], + ]; + $checkout->steps = []; + $checkout->step_name = null; + + unset($_REQUEST['pre-flight'], $_REQUEST['checkout_form']); + + $rules = $checkout->get_validation_rules(); + + // template_id should still have its base rule but NOT min:1 + $this->assertArrayHasKey('template_id', $rules); + $this->assertStringNotContainsString('min:1', $rules['template_id']); + } + + /** + * Test base template_id rule (integer|site_template) allows 0 + * when no template_selection field is present. + * + * This confirms admin/network site creation can still use template_id=0. + */ + public function test_validate_allows_template_id_zero_without_template_selection_field(): void { + + $checkout = Checkout::get_instance(); + $checkout->step = ['fields' => []]; + $checkout->steps = []; + $checkout->step_name = null; + + $this->ensure_session($checkout); + + $_REQUEST['template_id'] = 0; + + // Base rule without required or min:1 + $result = $checkout->validate(['template_id' => 'integer|site_template']); + + $this->assertTrue($result, 'template_id=0 should pass with base rule (admin/network context)'); + + unset($_REQUEST['template_id']); + } + + /** + * Test that a non-template required field still maps to itself. + * + * Ensures the field_to_rule_key mapping only affects template_selection + * and does not break other required fields. + */ + public function test_get_validation_rules_non_template_required_field_maps_to_itself(): void { + + $checkout = Checkout::get_instance(); + $checkout->step = [ + 'fields' => [ + ['id' => 'site_title', 'required' => true], + ], + ]; + $checkout->steps = []; + $checkout->step_name = null; + + unset($_REQUEST['pre-flight'], $_REQUEST['checkout_form']); + + $rules = $checkout->get_validation_rules(); + + $this->assertArrayHasKey('site_title', $rules); + $this->assertStringContainsString('required', $rules['site_title']); + // min:1 should NOT be added to non-template fields + $this->assertStringNotContainsString('min:1', $rules['site_title']); + } + // ------------------------------------------------------------------------- // Teardown // ------------------------------------------------------------------------- diff --git a/tests/WP_Ultimo/Dashboard_Widgets_Test.php b/tests/WP_Ultimo/Dashboard_Widgets_Test.php index 14bd7da5..e32fb708 100644 --- a/tests/WP_Ultimo/Dashboard_Widgets_Test.php +++ b/tests/WP_Ultimo/Dashboard_Widgets_Test.php @@ -114,7 +114,11 @@ public function test_enqueue_scripts_skips_non_index_page(): void { } /** - * Test enqueue_scripts enqueues activity-stream with correct deps on index.php. + * Test enqueue_scripts enqueues activity-stream on the network admin index.php. + * + * Since PR #785, enqueue_scripts() guards with is_network_admin() so the + * activity-stream assets are only loaded on the network dashboard — not + * the per-site dashboard. The test must simulate the network admin context. */ public function test_enqueue_scripts_enqueues_activity_stream_on_index(): void { @@ -129,6 +133,9 @@ public function test_enqueue_scripts_enqueues_activity_stream_on_index(): void { $wp_scripts->done = []; } + // Simulate the network admin so is_network_admin() returns true. + set_current_screen('dashboard-network'); + // Ensure wu-functions is registered so the dependency chain resolves. \WP_Ultimo\Scripts::get_instance()->register_default_scripts(); @@ -137,7 +144,7 @@ public function test_enqueue_scripts_enqueues_activity_stream_on_index(): void { $this->assertTrue( wp_script_is('wu-activity-stream', 'enqueued'), - 'wu-activity-stream should be enqueued on index.php' + 'wu-activity-stream should be enqueued on the network admin index.php' ); // Verify wu-functions and moment are declared dependencies. @@ -152,6 +159,43 @@ public function test_enqueue_scripts_enqueues_activity_stream_on_index(): void { $pagenow = $original; } + /** + * Test enqueue_scripts does NOT enqueue activity-stream on per-site dashboard. + * + * PR #785 added an is_network_admin() guard to prevent the Vue + * "Cannot find element: #activity-stream-content" console error + * on the per-site /wp-admin/index.php. + */ + public function test_enqueue_scripts_skips_activity_stream_on_per_site_dashboard(): void { + + global $pagenow, $wp_scripts; + + $original = $pagenow; + $original_queue = isset($wp_scripts) ? $wp_scripts->queue : []; + $pagenow = 'index.php'; + + if (isset($wp_scripts)) { + $wp_scripts->queue = []; + $wp_scripts->done = []; + } + + // Simulate the per-site dashboard (not network admin). + set_current_screen('dashboard'); + + $instance = $this->get_instance(); + $instance->enqueue_scripts(); + + $this->assertFalse( + wp_script_is('wu-activity-stream', 'enqueued'), + 'wu-activity-stream should NOT be enqueued on the per-site dashboard' + ); + + if (isset($wp_scripts)) { + $wp_scripts->queue = $original_queue; + } + $pagenow = $original; + } + /** * Test get_registered_dashboard_widgets returns array. *