/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ assertTrue(has_action('wp_head'), 'wp_head hook should have registered actions'); // Check if our specific CSS injection hook is registered $wp_head_callbacks = $GLOBALS['wp_filter']['wp_head']->callbacks; $found_css_injection = false; foreach ($wp_head_callbacks as $priority => $callbacks) { foreach ($callbacks as $callback) { if (is_array($callback['function']) && isset($callback['function'][0]) && is_object($callback['function'][0]) && method_exists($callback['function'][0], 'inject_restriction_css')) { $found_css_injection = true; $this->assertEquals(20, $priority, 'CSS injection should have priority 20 (after theme styles)'); break 2; } } } $this->assertTrue($found_css_injection, 'CSS injection callback should be registered on wp_head'); } /** * Test CSS injection generates correct styles for blocked doctors */ public function test_css_injection_blocked_doctors() { // Create test restrictions $this->create_test_doctor_restriction(999, true); $this->create_test_doctor_restriction(998, true); $this->create_test_doctor_restriction(997, false); // Not blocked // Capture CSS output ob_start(); do_action('wp_head'); $head_output = ob_get_clean(); // Should contain CSS for blocked doctors $this->assertStringContainsString('.kivicare-doctor[data-doctor-id="999"]', $head_output, 'Should contain CSS selector for blocked doctor 999'); $this->assertStringContainsString('.kivicare-doctor[data-doctor-id="998"]', $head_output, 'Should contain CSS selector for blocked doctor 998'); $this->assertStringNotContainsString('.kivicare-doctor[data-doctor-id="997"]', $head_output, 'Should NOT contain CSS selector for non-blocked doctor 997'); // Should contain display: none directive $this->assertStringContainsString('display: none !important;', $head_output, 'Should contain display: none !important directive'); // Should be wrapped in style tags with proper data attribute $this->assertStringContainsString('', $head_output, 'Should contain closing style tag'); } /** * Test CSS injection generates correct styles for blocked services */ public function test_css_injection_blocked_services() { // Create test service restrictions $this->create_test_service_restriction(888, 999, true); // Block service 888 for doctor 999 $this->create_test_service_restriction(887, 998, true); // Block service 887 for doctor 998 $this->create_test_service_restriction(886, 999, false); // Don't block service 886 for doctor 999 // Capture CSS output ob_start(); do_action('wp_head'); $head_output = ob_get_clean(); // Should contain CSS for blocked services with doctor context $this->assertStringContainsString('.kivicare-service[data-service-id="888"][data-doctor-id="999"]', $head_output, 'Should contain CSS selector for service 888 blocked for doctor 999'); $this->assertStringContainsString('.kivicare-service[data-service-id="887"][data-doctor-id="998"]', $head_output, 'Should contain CSS selector for service 887 blocked for doctor 998'); // Should NOT contain CSS for non-blocked service $this->assertStringNotContainsString('[data-service-id="886"]', $head_output, 'Should NOT contain CSS selector for non-blocked service 886'); // Should contain display: none directive $this->assertStringContainsString('display: none !important;', $head_output); } /** * Test CSS injection includes fallback selectors */ public function test_css_injection_fallback_selectors() { // Create test restrictions $this->create_test_doctor_restriction(999, true); $this->create_test_service_restriction(888, 999, true); // Capture CSS output ob_start(); do_action('wp_head'); $head_output = ob_get_clean(); // Should include fallback ID selectors $this->assertStringContainsString('#doctor-999', $head_output, 'Should include fallback ID selector for doctor'); $this->assertStringContainsString('#service-888-doctor-999', $head_output, 'Should include fallback ID selector for service'); // Should include fallback option selectors $this->assertStringContainsString('.doctor-selection option[value="999"]', $head_output, 'Should include fallback option selector for doctor'); $this->assertStringContainsString('.service-selection option[value="888"]', $head_output, 'Should include fallback option selector for service'); } /** * Test CSS injection handles empty restrictions */ public function test_css_injection_empty_restrictions() { // No restrictions created // Capture CSS output ob_start(); do_action('wp_head'); $head_output = ob_get_clean(); // Should still output style tags but with minimal content if (strpos($head_output, '', $head_output); // Content should be minimal (just comments or empty) $style_content = $this->extract_style_content($head_output); $this->assertLessThan(100, strlen(trim($style_content)), 'Style content should be minimal when no restrictions exist'); } else { // Or no style output at all is also acceptable $this->assertStringNotContainsString('data-care-booking', $head_output, 'No CSS should be output when no restrictions exist'); } } /** * Test CSS injection uses cache for performance */ public function test_css_injection_uses_cache() { // Create test restrictions $this->create_test_doctor_restriction(999, true); // Pre-populate cache $blocked_doctors = [999]; $blocked_services = []; set_transient('care_booking_doctors_blocked', $blocked_doctors, 3600); // Measure performance with cache $start_time = microtime(true); ob_start(); do_action('wp_head'); $head_output = ob_get_clean(); $end_time = microtime(true); $execution_time = ($end_time - $start_time) * 1000; // Should be very fast with cache (under 50ms) $this->assertLessThan(50, $execution_time, 'CSS injection should be fast with cache'); // Should contain correct CSS $this->assertStringContainsString('.kivicare-doctor[data-doctor-id="999"]', $head_output); } /** * Test CSS injection handles database errors gracefully */ public function test_css_injection_handles_database_errors() { // Create test restrictions first $this->create_test_doctor_restriction(999, true); // Mock database error global $wpdb; $original_prefix = $wpdb->prefix; $wpdb->prefix = 'invalid_prefix_'; // Clear cache to force database query delete_transient('care_booking_doctors_blocked'); // CSS injection should handle error gracefully ob_start(); do_action('wp_head'); $head_output = ob_get_clean(); // Restore prefix $wpdb->prefix = $original_prefix; // Should not throw fatal errors $this->assertTrue(true, 'CSS injection should handle database errors without fatal errors'); // May contain minimal or no CSS output due to error if (strpos($head_output, 'assertStringContainsString('create_test_doctor_restriction(999, true); // Capture CSS output ob_start(); do_action('wp_head'); $head_output = ob_get_clean(); // Should not contain any unescaped content $this->assertStringNotContainsString('assertStringNotContainsString('javascript:', $head_output, 'Should not contain javascript: protocol'); $this->assertStringNotContainsString('expression(', $head_output, 'Should not contain CSS expressions'); // Should contain proper CSS syntax $this->assertRegExp('/\{[^}]*display:\s*none\s*!important[^}]*\}/', $head_output, 'Should contain proper CSS syntax for display:none'); } /** * Test CSS injection only occurs on frontend pages */ public function test_css_injection_frontend_only() { $this->create_test_doctor_restriction(999, true); // Test admin context set_current_screen('edit-post'); ob_start(); do_action('wp_head'); $admin_output = ob_get_clean(); // Test frontend context set_current_screen('front'); ob_start(); do_action('wp_head'); $frontend_output = ob_get_clean(); // CSS should be injected on frontend but policy may vary for admin // At minimum, it should work on frontend if (strpos($frontend_output, '