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:
parent
0b61643ec5
commit
b8e6d2537d
20 changed files with 1331 additions and 33 deletions
134
tests/Unit/PetitionMapTest.php
Normal file
134
tests/Unit/PetitionMapTest.php
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
// petition-map.php defines cache-path constants and functions.
|
||||
// petitionMapBuildData() takes an explicit $csvPath so it can be tested
|
||||
// with temp files; the cache constants are not touched by these tests.
|
||||
//
|
||||
// Context is already stubbed in PetitionFormTest.php (loaded first alphabetically),
|
||||
// so we do not redeclare it here.
|
||||
require_once CUSTOM_DIR . '/plugins/page/petition-map.php';
|
||||
|
||||
// CSV header row shared across tests
|
||||
const PETITION_MAP_CSV_HEADER = ['timestamp', 'email', 'firstname', 'surname', 'region', 'display', 'status', 'token', 'token_created', 'ip_hash'];
|
||||
|
||||
/**
|
||||
* Write rows to a temp file and return its path.
|
||||
* The caller is responsible for unlinking it.
|
||||
*/
|
||||
function mapTestCsv(array $rows): string
|
||||
{
|
||||
$path = tempnam(sys_get_temp_dir(), 'map_test_');
|
||||
$fp = fopen($path, 'w');
|
||||
foreach ($rows as $row) {
|
||||
fputcsv($fp, $row, ',', '"', '');
|
||||
}
|
||||
fclose($fp);
|
||||
return $path;
|
||||
}
|
||||
|
||||
// --- petitionMapBuildData: missing / empty file ---
|
||||
|
||||
it('returns empty data when CSV does not exist', function () {
|
||||
$result = petitionMapBuildData('/tmp/nonexistent-petition-map-test.csv');
|
||||
expect($result['total'])->toBe(0);
|
||||
expect($result)->toHaveKey('generated');
|
||||
});
|
||||
|
||||
it('returns zero total for a CSV with only pending signatures', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'a@b.com', 'Ola', 'Hansen', 'oslo', 'semi', 'pending', 'tok1', time(), 'hash1'],
|
||||
]);
|
||||
expect(petitionMapBuildData($path)['total'])->toBe(0);
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
it('returns zero total for a CSV with only the header', function () {
|
||||
$path = mapTestCsv([PETITION_MAP_CSV_HEADER]);
|
||||
expect(petitionMapBuildData($path)['total'])->toBe(0);
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
// --- petitionMapBuildData: counting confirmed signatures ---
|
||||
|
||||
it('counts confirmed signatures and groups them by region', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||||
[time(), 'b@b.com', 'Ola', 'Nilsen', 'oslo', 'semi', 'confirmed', 't2', time(), 'h2'],
|
||||
[time(), 'c@b.com', 'Per', 'Olsen', 'bergen', 'semi', 'confirmed', 't3', time(), 'h3'],
|
||||
]);
|
||||
$result = petitionMapBuildData($path);
|
||||
expect($result['total'])->toBe(3);
|
||||
expect($result['regions']['oslo']['count'])->toBe(2);
|
||||
expect($result['regions']['bergen']['count'])->toBe(1);
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
it('ignores non-confirmed rows (pending, deleted, etc.)', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||||
[time(), 'b@b.com', 'Ola', 'Nilsen', 'oslo', 'semi', 'pending', 't2', time(), 'h2'],
|
||||
[time(), 'c@b.com', 'Per', 'Olsen', 'oslo', 'semi', 'deleted', 't3', time(), 'h3'],
|
||||
]);
|
||||
$result = petitionMapBuildData($path);
|
||||
expect($result['total'])->toBe(1);
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
it('skips confirmed entries that have no region', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'a@b.com', 'Kari', 'Hansen', '', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||||
]);
|
||||
expect(petitionMapBuildData($path)['total'])->toBe(0);
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
// --- petitionMapBuildData: name handling ---
|
||||
|
||||
it('uses only the first word of the firstname', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'a@b.com', 'Kari Marte', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||||
]);
|
||||
$signer = petitionMapBuildData($path)['regions']['oslo']['signers'][0];
|
||||
expect($signer['n'])->toBe('Kari');
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
it('sets name to null and anonymous flag true for anonymous display', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'anonymous', 'confirmed', 't1', time(), 'h1'],
|
||||
]);
|
||||
$signer = petitionMapBuildData($path)['regions']['oslo']['signers'][0];
|
||||
expect($signer['n'])->toBeNull();
|
||||
expect($signer['a'])->toBeTrue();
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
it('sets anonymous flag false for non-anonymous display', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'a@b.com', 'Lars', 'Berg', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||||
]);
|
||||
$signer = petitionMapBuildData($path)['regions']['oslo']['signers'][0];
|
||||
expect($signer['a'])->toBeFalse();
|
||||
unlink($path);
|
||||
});
|
||||
|
||||
it('does not include emails, surnames, tokens, or ip_hashes in the output', function () {
|
||||
$path = mapTestCsv([
|
||||
PETITION_MAP_CSV_HEADER,
|
||||
[time(), 'secret@b.com', 'Lars', 'SecretSurname', 'oslo', 'semi', 'confirmed', 'secret-token', time(), 'secret-hash'],
|
||||
]);
|
||||
$result = petitionMapBuildData($path);
|
||||
$json = json_encode($result);
|
||||
expect($json)->not->toContain('secret@b.com');
|
||||
expect($json)->not->toContain('SecretSurname');
|
||||
expect($json)->not->toContain('secret-token');
|
||||
expect($json)->not->toContain('secret-hash');
|
||||
unlink($path);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue