- 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>
353 lines
14 KiB
PHP
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');
|
|
}
|
|
} |