Files
care-book-block-ultimate/BACKUP-ESSENTIALS/PRODUCTION-READY/care-booking-block-ultimate/tests/integration/test-enhanced-css-injection.php
Emanuel Almeida 38bb926742 chore: add spec-kit and standardize signatures
- Added GitHub spec-kit for development workflow
- Standardized file signatures to Descomplicar® format
- Updated development configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 01:27:34 +01:00

353 lines
14 KiB
PHP

/**
* Descomplicar® Crescimento Digital
* https://descomplicar.pt
*/
<?php
/**
* Integration test for enhanced CSS injection (T031, T033)
*
* @package CareBookingBlock
*/
/**
* Test enhanced CSS injection with optimization and caching
*/
class Test_Enhanced_CSS_Injection extends Care_Booking_Test_Case
{
/**
* Test wp_head hook has correct priority
*/
public function test_wp_head_hook_priority()
{
$this->assertTrue(has_action('wp_head'), 'wp_head hook should have registered actions');
// Check priority is 15 (after theme styles)
$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(15, $priority, 'CSS injection should have priority 15');
break 2;
}
}
}
$this->assertTrue($found_css_injection, 'CSS injection callback should be registered on wp_head');
}
/**
* Test enhanced CSS generation with caching
*/
public function test_enhanced_css_generation_with_caching()
{
// Create test restrictions
$this->create_test_doctor_restriction(999, true);
$this->create_test_service_restriction(888, 999, true);
$integration = $this->plugin->kivicare_integration;
// Use reflection to access private method
$reflection = new ReflectionClass($integration);
$method = $reflection->getMethod('generate_restriction_css');
$method->setAccessible(true);
// Clear cache first
delete_transient('care_booking_css_' . md5(serialize([[999], [['service_id' => 888, 'doctor_id' => 999]]])));
// First call should generate and cache CSS
$start_time = microtime(true);
$css1 = $method->invoke($integration, [999], [['service_id' => 888, 'doctor_id' => 999]]);
$end_time = microtime(true);
$time1 = ($end_time - $start_time) * 1000;
// Second call should use cache
$start_time = microtime(true);
$css2 = $method->invoke($integration, [999], [['service_id' => 888, 'doctor_id' => 999]]);
$end_time = microtime(true);
$time2 = ($end_time - $start_time) * 1000;
$this->assertEquals($css1, $css2, 'Cached CSS should be identical');
$this->assertLessThan($time1, $time2, 'Cached call should be faster');
$this->assertLessThan(10, $time2, 'Cached call should be very fast');
}
/**
* Test enhanced CSS selectors for KiviCare 3.0+
*/
public function test_enhanced_css_selectors()
{
// Create test restrictions
$this->create_test_doctor_restriction(999, true);
$this->create_test_service_restriction(888, 999, true);
$integration = $this->plugin->kivicare_integration;
// Use reflection to access private method
$reflection = new ReflectionClass($integration);
$method = $reflection->getMethod('generate_restriction_css');
$method->setAccessible(true);
$css = $method->invoke($integration, [999], [['service_id' => 888, 'doctor_id' => 999]]);
// Check for KiviCare 3.0+ selectors
$this->assertStringContainsString('.kc-doctor-item[data-id="999"]', $css, 'Should contain KiviCare 3.0 doctor selector');
$this->assertStringContainsString('.doctor-card[data-doctor="999"]', $css, 'Should contain modern doctor card selector');
$this->assertStringContainsString('.kc-service-item[data-service="888"][data-doctor="999"]', $css, 'Should contain KiviCare 3.0 service selector');
// Check for form selectors
$this->assertStringContainsString('select[name=\'doctor_id\'] option[value="999"]', $css, 'Should contain form option selector');
$this->assertStringContainsString('.service-selection[data-doctor="999"] option[value="888"]', $css, 'Should contain contextual service selector');
// Check for booking form selectors
$this->assertStringContainsString('.booking-doctor-999', $css, 'Should contain booking doctor selector');
$this->assertStringContainsString('.appointment-service-888.doctor-999', $css, 'Should contain appointment service selector');
}
/**
* Test CSS chunking for performance
*/
public function test_css_chunking_performance()
{
// Create many doctor restrictions
$doctor_ids = [];
for ($i = 1000; $i <= 1150; $i++) {
$this->create_test_doctor_restriction($i, true);
$doctor_ids[] = $i;
}
$integration = $this->plugin->kivicare_integration;
// Use reflection to access private method
$reflection = new ReflectionClass($integration);
$method = $reflection->getMethod('generate_restriction_css');
$method->setAccessible(true);
$css = $method->invoke($integration, $doctor_ids, []);
// Should contain multiple CSS rules (chunked)
$rule_count = substr_count($css, 'display: none !important;');
$this->assertGreaterThan(1, $rule_count, 'Should chunk selectors into multiple CSS rules');
$this->assertLessThan(10, $rule_count, 'Should not create too many rules');
// CSS should be reasonably sized
$this->assertLessThan(100000, strlen($css), 'Generated CSS should be reasonably sized');
}
/**
* Test CSS minification in production
*/
public function test_css_minification()
{
// Create test restrictions
$this->create_test_doctor_restriction(999, true);
$integration = $this->plugin->kivicare_integration;
// Use reflection to access private method
$reflection = new ReflectionClass($integration);
$method = $reflection->getMethod('minify_css');
$method->setAccessible(true);
$verbose_css = "/* Comment */\n.test {\n display: none !important;\n visibility: hidden !important;\n}";
$minified = $method->invoke($integration, $verbose_css);
$this->assertStringNotContainsString('/*', $minified, 'Comments should be removed');
$this->assertStringNotContainsString("\n", $minified, 'Line breaks should be removed');
$this->assertStringNotContainsString(" ", $minified, 'Multiple spaces should be removed');
$this->assertStringContainsString('display:none!important', $minified, 'Properties should be compressed');
}
/**
* Test conditional CSS injection based on page content
*/
public function test_conditional_css_injection()
{
// Create test restrictions
$this->create_test_doctor_restriction(999, true);
$integration = $this->plugin->kivicare_integration;
// Use reflection to access private method
$reflection = new ReflectionClass($integration);
$method = $reflection->getMethod('should_inject_css');
$method->setAccessible(true);
// Mock KiviCare as active
$reflection_active = new ReflectionClass($integration);
$active_method = $reflection_active->getMethod('is_kivicare_active');
$active_method->setAccessible(true);
// Test with page that should load scripts
global $post;
$post = (object) ['post_content' => '[kivicare_booking]'];
// Should inject CSS
$should_inject = $method->invoke($integration);
// Note: This might be false if KiviCare is not actually active in test environment
// Test with page that shouldn't load scripts
$post = (object) ['post_content' => 'Regular page content'];
$should_not_inject = $method->invoke($integration);
// This test depends on KiviCare being active, so we'll just ensure method doesn't crash
$this->assertTrue(is_bool($should_not_inject), 'should_inject_css should return boolean');
}
/**
* Test graceful degradation CSS classes
*/
public function test_graceful_degradation_css()
{
// Create test restrictions
$this->create_test_doctor_restriction(999, true);
$integration = $this->plugin->kivicare_integration;
// Use reflection to access private method
$reflection = new ReflectionClass($integration);
$method = $reflection->getMethod('generate_restriction_css');
$method->setAccessible(true);
$css = $method->invoke($integration, [999], []);
// Should contain fallback classes
$this->assertStringContainsString('.care-booking-fallback', $css, 'Should contain fallback class');
$this->assertStringContainsString('.care-booking-loading::after', $css, 'Should contain loading class');
$this->assertStringContainsString('opacity: 0.7', $css, 'Should contain fallback styling');
$this->assertStringContainsString('pointer-events: none', $css, 'Should disable pointer events for fallback');
}
/**
* Test CSS injection with version attribute
*/
public function test_css_injection_versioning()
{
// Create test restrictions
$this->create_test_doctor_restriction(999, true);
// Capture CSS output
ob_start();
do_action('wp_head');
$head_output = ob_get_clean();
if (strpos($head_output, 'care-booking-restrictions') !== false) {
// Should contain version attribute
$this->assertStringContainsString('data-version="' . CARE_BOOKING_BLOCK_VERSION . '"', $head_output,
'Should contain version attribute');
// Should contain proper ID
$this->assertStringContainsString('id="care-booking-restrictions"', $head_output,
'Should contain proper style ID');
// Should contain HTML comments for debugging
$this->assertStringContainsString('<!-- Care Booking Block Styles -->', $head_output,
'Should contain start comment');
$this->assertStringContainsString('<!-- End Care Booking Block Styles -->', $head_output,
'Should contain end comment');
}
}
/**
* Test error handling in CSS injection
*/
public function test_css_injection_error_handling()
{
// Create test restrictions first
$this->create_test_doctor_restriction(999, true);
// Force an error by mocking a database issue
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 contain PHP errors
$this->assertStringNotContainsString('Fatal error', $head_output, 'Should not contain fatal errors');
$this->assertStringNotContainsString('Warning:', $head_output, 'Should not contain warnings');
// In debug mode, should contain error comment
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->assertStringContainsString('CSS injection failed', $head_output,
'Should contain error comment in debug mode');
}
}
/**
* Test CSS injection performance
*/
public function test_css_injection_performance()
{
// Create moderate number of restrictions
for ($i = 1000; $i <= 1050; $i++) {
$this->create_test_doctor_restriction($i, true);
}
for ($i = 2000; $i <= 2025; $i++) {
$this->create_test_service_restriction($i, 1000, true);
}
$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 complete quickly (under 200ms)
$this->assertLessThan(200, $execution_time, 'CSS injection should be performant');
// Generated CSS should be reasonable size
if (strpos($head_output, 'care-booking-restrictions') !== false) {
$this->assertLessThan(50000, strlen($head_output), 'Generated CSS should be reasonably sized');
}
}
/**
* Test CSS cache invalidation
*/
public function test_css_cache_invalidation()
{
// Create initial restriction
$this->create_test_doctor_restriction(999, true);
$integration = $this->plugin->kivicare_integration;
// Use reflection to access private method
$reflection = new ReflectionClass($integration);
$method = $reflection->getMethod('generate_restriction_css');
$method->setAccessible(true);
// Generate initial CSS
$css1 = $method->invoke($integration, [999], []);
$this->assertStringContainsString('999', $css1, 'Initial CSS should contain doctor 999');
// Add new restriction
$this->create_test_doctor_restriction(998, true);
// Cache should be invalidated and new CSS should include both doctors
$css2 = $method->invoke($integration, [999, 998], []);
$this->assertStringContainsString('999', $css2, 'Updated CSS should contain doctor 999');
$this->assertStringContainsString('998', $css2, 'Updated CSS should contain doctor 998');
$this->assertNotEquals($css1, $css2, 'CSS should be different after adding restriction');
}
}