Files
care-book-block-ultimate/PRODUCTION-READY/care-booking-block-ultimate/tests/integration/test-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

388 lines
15 KiB
PHP

/**
* Descomplicar® Crescimento Digital
* https://descomplicar.pt
*/
<?php
/**
* Integration test for CSS injection on wp_head
*
* @package CareBookingBlock
*/
/**
* Test CSS injection functionality on wp_head hook
*/
class Test_CSS_Injection extends Care_Booking_Test_Case
{
/**
* Test wp_head hook is registered for CSS injection
*/
public function test_wp_head_hook_registered()
{
$this->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('<style data-care-booking>', $head_output,
'Should contain opening style tag with data attribute');
$this->assertStringContainsString('</style>', $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, '<style data-care-booking>') !== false) {
$this->assertStringContainsString('<style data-care-booking>', $head_output);
$this->assertStringContainsString('</style>', $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, '<style') !== false) {
$this->assertStringContainsString('<style', $head_output, 'Should contain style tags even on error');
}
}
/**
* Test CSS injection output is properly escaped and secure
*/
public function test_css_injection_security()
{
// Create test restrictions with edge case IDs
$this->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('<script', $head_output, 'Should not contain script tags');
$this->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, '<style data-care-booking>') !== false) {
$this->assertStringContainsString('.kivicare-doctor[data-doctor-id="999"]', $frontend_output,
'CSS should be injected on frontend');
}
// Admin behavior may vary based on implementation
$this->assertTrue(true, 'CSS injection should handle admin vs frontend context appropriately');
}
/**
* Test CSS injection performance with large restriction sets
*/
public function test_css_injection_performance_large_dataset()
{
// Create many restrictions
for ($i = 1000; $i <= 1100; $i++) {
$this->create_test_doctor_restriction($i, true);
}
for ($i = 2000; $i <= 2050; $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 in reasonable time even with large datasets (under 200ms)
$this->assertLessThan(200, $execution_time, 'CSS injection should handle large datasets efficiently');
// Should contain CSS for many restrictions
$this->assertStringContainsString('.kivicare-doctor[data-doctor-id="1000"]', $head_output);
$this->assertStringContainsString('.kivicare-service[data-service-id="2000"]', $head_output);
// CSS should be reasonably sized (under 100KB)
$this->assertLessThan(100000, strlen($head_output), 'Generated CSS should be reasonably sized');
}
/**
* Test CSS injection minification and optimization
*/
public function test_css_injection_optimization()
{
// Create test restrictions
$this->create_test_doctor_restriction(999, true);
$this->create_test_doctor_restriction(998, true);
$this->create_test_service_restriction(888, 999, true);
ob_start();
do_action('wp_head');
$head_output = ob_get_clean();
$style_content = $this->extract_style_content($head_output);
// Should combine selectors efficiently
$doctor_selectors = substr_count($style_content, '.kivicare-doctor');
$this->assertGreaterThan(0, $doctor_selectors, 'Should contain doctor selectors');
// Should minimize redundant CSS
$display_none_count = substr_count($style_content, 'display: none !important');
$this->assertGreaterThan(0, $display_none_count, 'Should contain display:none declarations');
// Should not contain excessive whitespace if minified
if (strpos($style_content, ' ') === false) {
$this->assertTrue(true, 'CSS appears to be minified');
}
}
/**
* Test CSS injection cache invalidation
*/
public function test_css_injection_cache_invalidation()
{
// Create initial restriction
$this->create_test_doctor_restriction(999, true);
// Generate initial CSS
ob_start();
do_action('wp_head');
$initial_output = ob_get_clean();
$this->assertStringContainsString('data-doctor-id="999"', $initial_output);
// Add new restriction (should invalidate cache)
$this->create_test_doctor_restriction(998, true);
// Simulate cache invalidation
delete_transient('care_booking_doctors_blocked');
delete_transient('care_booking_restrictions_hash');
// Generate CSS again
ob_start();
do_action('wp_head');
$updated_output = ob_get_clean();
// Should now include both doctors
$this->assertStringContainsString('data-doctor-id="999"', $updated_output);
$this->assertStringContainsString('data-doctor-id="998"', $updated_output);
}
/**
* Helper method to extract style content from HTML
*/
private function extract_style_content($html)
{
if (preg_match('/<style[^>]*data-care-booking[^>]*>(.*?)<\/style>/s', $html, $matches)) {
return $matches[1];
}
return '';
}
}