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>
This commit is contained in:
@@ -0,0 +1,388 @@
|
||||
/**
|
||||
* 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 '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user