PHP Classes

File: tests/run-tests.php

Recommend this page to a friend!
  Packages of AKONO Metsam Nathan   Theme Sync - Server-Side UI Preference Detector   tests/run-tests.php   Download  
File: tests/run-tests.php
Role: Example script
Content type: text/plain
Description: Dependency-free automated test suite covering Client Hints detection, cookie fallback, and CSS output sanitization.
Class: Theme Sync - Server-Side UI Preference Detector
Detect dark mode, contrast and motion preferences
Author: By
Last change:
Date: 15 days ago
Size: 5,065 bytes
 

Contents

Class file image Download
<?php

declare(strict_types=1);

require
__DIR__ . '/../src/ThemePreferenceDetector.php';

use
ThemeSync\ThemePreferenceDetector;

$failures = 0;
$passed = 0;

/**
 * @param mixed $expected
 * @param mixed $actual
 */
function assertSame_($expected, $actual, string $label): void
{
    global
$failures, $passed;
    if (
$expected === $actual) {
       
$passed++;
        echo
" [OK] {$label}\n";
    } else {
       
$failures++;
        echo
" [FAIL] {$label}\n";
        echo
' expected: ' . var_export($expected, true) . "\n";
        echo
' actual: ' . var_export($actual, true) . "\n";
    }
}

function
assertTrue_(bool $condition, string $label): void
{
   
assertSame_(true, $condition, $label);
}

// --- Test 1: defaults are used with no hints and no cookie -----------------
echo "Test 1: defaults with no signals\n";
$detector = new ThemePreferenceDetector([], [], []);
assertSame_('light', $detector->getPreference('color-scheme'), 'defaults to light');
assertTrue_($detector->isUsingDefaults(), 'isUsingDefaults() is true');
assertSame_(false, $detector->wasDetectedFromClientHints(), 'not from client hints');
assertSame_(false, $detector->wasDetectedFromCookie(), 'not from cookie');

// --- Test 2: custom defaults are respected ----------------------------------
echo "\nTest 2: custom defaults\n";
$detector = new ThemePreferenceDetector(['color-scheme' => 'dark'], [], []);
assertSame_('dark', $detector->getPreference('color-scheme'), 'custom default applied');
assertTrue_($detector->isDarkMode(), 'isDarkMode() true with custom default');

// --- Test 3: Client Hints take priority -------------------------------------
echo "\nTest 3: client hints detection\n";
$server = [
   
'HTTP_SEC_CH_PREFERS_COLOR_SCHEME' => 'dark',
   
'HTTP_SEC_CH_PREFERS_CONTRAST' => 'more',
   
'HTTP_SEC_CH_PREFERS_REDUCED_MOTION' => 'reduce',
];
$cookies = [
   
'tsync_prefs' => json_encode(['color-scheme' => 'light']),
];
$detector = new ThemePreferenceDetector([], $server, $cookies);
assertTrue_($detector->wasDetectedFromClientHints(), 'flagged as from client hints');
assertSame_(false, $detector->wasDetectedFromCookie(), 'cookie ignored when hints present');
assertTrue_($detector->isDarkMode(), 'dark mode from client hint, not cookie');
assertTrue_($detector->prefersMoreContrast(), 'more contrast from client hint');
assertTrue_($detector->prefersReducedMotion(), 'reduced motion from client hint');
assertSame_('no-preference', $detector->getPreference('reduced-transparency'), 'missing hint falls back to default');

// --- Test 4: cookie fallback when no Client Hints ---------------------------
echo "\nTest 4: cookie fallback\n";
$cookies = [
   
'tsync_prefs' => json_encode([
       
'color-scheme' => 'dark',
       
'reduced-motion' => 'reduce',
    ]),
];
$detector = new ThemePreferenceDetector([], [], $cookies);
assertTrue_($detector->wasDetectedFromCookie(), 'flagged as from cookie');
assertSame_(false, $detector->wasDetectedFromClientHints(), 'not flagged as from client hints');
assertTrue_($detector->isDarkMode(), 'dark mode from cookie');
assertTrue_($detector->prefersReducedMotion(), 'reduced motion from cookie');
assertSame_('no-preference', $detector->getPreference('contrast'), 'missing cookie key falls back to default');

// --- Test 5: malformed cookie is ignored gracefully -------------------------
echo "\nTest 5: malformed cookie\n";
$detector = new ThemePreferenceDetector([], [], ['tsync_prefs' => 'not-json{{{']);
assertTrue_($detector->isUsingDefaults(), 'malformed cookie falls back to defaults without error');

// --- Test 6: CSS output is well-formed and value-escaped --------------------
echo "\nTest 6: CSS variable output\n";
$server = ['HTTP_SEC_CH_PREFERS_COLOR_SCHEME' => 'dark; } body { color: red'];
$detector = new ThemePreferenceDetector([], $server, []);
$css = $detector->toCssVariables();
assertTrue_(str_contains($css, ':root {'), 'wraps in :root block');
assertTrue_(!str_contains($css, ';}'), 'injected characters stripped, no premature block close');
assertTrue_(!str_contains($css, '{ color'), 'CSS injection payload neutralized');

// --- Test 7: custom CSS variable mapping ------------------------------------
echo "\nTest 7: custom variable mapping\n";
$detector = new ThemePreferenceDetector(['color-scheme' => 'dark'], [], []);
$css = $detector->toCssVariables(['color-scheme' => '--app-theme']);
assertTrue_(str_contains($css, '--app-theme: dark;'), 'custom mapping used');

// --- Test 8: bootstrap script is valid-looking JS ---------------------------
echo "\nTest 8: bootstrap script\n";
$detector = new ThemePreferenceDetector();
$script = $detector->getBootstrapScript();
assertTrue_(str_starts_with($script, '<script>'), 'starts with <script>');
assertTrue_(str_ends_with($script, '</script>'), 'ends with </script>');
assertTrue_(str_contains($script, 'tsync_prefs'), 'references the cookie name');
assertTrue_(str_contains($script, 'matchMedia'), 'uses matchMedia');

echo
"\n---\n{$passed} passed, {$failures} failed\n";
exit(
$failures > 0 ? 1 : 0);