Add browser and unit test suite with Pest + Playwright

Add comprehensive test coverage for core site features (content,
navigation, language, FAQ, news, petition, newsletter) using Pest
browser tests and unit tests for custom plugins. Includes test
infrastructure (Containerfile.test, compose.test.yaml), test
documentation, and test files covering petition form logic, CSV
handling, translation, date formatting, rate limiting, and map data
building.
This commit is contained in:
Ruben 2026-03-17 16:43:39 +01:00
parent 0b61643ec5
commit b8e6d2537d
20 changed files with 1331 additions and 33 deletions

View file

@ -0,0 +1,60 @@
<?php
// Stubs for framework classes used by petition-form.php
class Context {}
class Hook { const TEMPLATE_VARS = 'template_vars'; }
class Hooks { public static function add(string $hook, callable $fn): void {} }
// petition-form.php calls session_start() at load time — stub $_SESSION and $_SERVER first
$_SESSION = [];
$_SERVER['REMOTE_ADDR'] ??= '127.0.0.1';
$_SERVER['HTTP_HOST'] ??= 'localhost';
require_once CUSTOM_DIR . '/plugins/page/petition-form.php';
// --- petitionSanitizeCSV ---
it('leaves safe values unchanged', function () {
expect(petitionSanitizeCSV('John Doe'))->toBe('John Doe');
expect(petitionSanitizeCSV('john@example.com'))->toBe('john@example.com');
expect(petitionSanitizeCSV(''))->toBe('');
});
it('prefixes formula-starting characters to prevent CSV injection', function () {
expect(petitionSanitizeCSV('=SUM(A1)'))->toBe("'=SUM(A1)");
expect(petitionSanitizeCSV('+evil'))->toBe("'+evil");
expect(petitionSanitizeCSV('-evil'))->toBe("'-evil");
expect(petitionSanitizeCSV('@evil'))->toBe("'@evil");
});
it('prefixes tab and newline characters', function () {
expect(petitionSanitizeCSV("\t data"))->toBe("'\t data");
expect(petitionSanitizeCSV("\r data"))->toBe("'\r data");
expect(petitionSanitizeCSV("\n data"))->toBe("'\n data");
});
it('only checks the first character', function () {
expect(petitionSanitizeCSV('safe=still safe'))->toBe('safe=still safe');
});
// --- petitionGetCsvPath ---
it('builds the correct CSV path for a valid petition ID', function () {
$path = petitionGetCsvPath('my-petition');
expect($path)->toEndWith('/data/petitions/my-petition.csv');
});
it('strips non-alphanumeric characters from petition ID', function () {
$path = petitionGetCsvPath('my petition!');
expect($path)->toEndWith('/data/petitions/mypetition.csv');
});
it('throws for an empty petition ID', function () {
petitionGetCsvPath('');
})->throws(Exception::class);
it('strips path traversal characters rather than throwing', function () {
// Dots and slashes are stripped by the regex, leaving a safe ID
$path = petitionGetCsvPath('../evil');
expect($path)->toEndWith('/data/petitions/evil.csv');
});