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
162
tests/Unit/PetitionFormTestPure.php
Normal file
162
tests/Unit/PetitionFormTestPure.php
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
// PetitionFormTest.php (which loads first alphabetically) already required
|
||||
// petition-form.php and defined Context / Hook / Hooks stubs.
|
||||
// This file only adds tests — no re-requires or re-declarations needed.
|
||||
|
||||
/**
|
||||
* Build a minimal Context mock that supports get('translations') and get('currentLang').
|
||||
*/
|
||||
function mockContext(array $translations = [], string $lang = 'no'): object
|
||||
{
|
||||
return new class($translations, $lang) extends Context {
|
||||
public function __construct(
|
||||
private array $translations,
|
||||
private string $lang
|
||||
) {}
|
||||
|
||||
public function get(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return match ($key) {
|
||||
'translations' => $this->translations,
|
||||
'currentLang' => $this->lang,
|
||||
default => $default,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --- petitionT ---
|
||||
|
||||
it('returns the key when no translation is found', function () {
|
||||
$ctx = mockContext([]);
|
||||
expect(petitionT($ctx, 'petition', 'missing_key'))->toBe('missing_key');
|
||||
});
|
||||
|
||||
it('returns the translated string when found', function () {
|
||||
$ctx = mockContext(['petition.greeting' => 'Hei!']);
|
||||
expect(petitionT($ctx, 'petition', 'greeting'))->toBe('Hei!');
|
||||
});
|
||||
|
||||
it('applies placeholder replacements', function () {
|
||||
$ctx = mockContext(['petition.email_greeting' => 'Hei {name}!']);
|
||||
expect(petitionT($ctx, 'petition', 'email_greeting', ['name' => 'Kari']))->toBe('Hei Kari!');
|
||||
});
|
||||
|
||||
it('applies multiple replacements in a single string', function () {
|
||||
$ctx = mockContext(['petition.thanks' => 'Takk {name} for {title}']);
|
||||
$result = petitionT($ctx, 'petition', 'thanks', ['name' => 'Ola', 'title' => 'kampanjen']);
|
||||
expect($result)->toBe('Takk Ola for kampanjen');
|
||||
});
|
||||
|
||||
it('uses the section as part of the translation key', function () {
|
||||
$ctx = mockContext(['regions.oslo' => 'Oslo', 'petition.oslo' => 'Wrong']);
|
||||
expect(petitionT($ctx, 'regions', 'oslo'))->toBe('Oslo');
|
||||
});
|
||||
|
||||
// --- petitionGetIdFromPath ---
|
||||
|
||||
it('returns the basename of a path as the petition ID', function () {
|
||||
expect(petitionGetIdFromPath('/content/underskriftskampanje/my-petition'))->toBe('my-petition');
|
||||
});
|
||||
|
||||
it('strips a leading numeric prefix from the folder name', function () {
|
||||
expect(petitionGetIdFromPath('/content/01-my-petition'))->toBe('my-petition');
|
||||
expect(petitionGetIdFromPath('/content/001-my-petition'))->toBe('my-petition');
|
||||
});
|
||||
|
||||
it('leaves non-prefixed names intact', function () {
|
||||
expect(petitionGetIdFromPath('/content/medisinsk-cannabis'))->toBe('medisinsk-cannabis');
|
||||
});
|
||||
|
||||
it('returns null when the basename is empty', function () {
|
||||
expect(petitionGetIdFromPath('/'))->toBeNull();
|
||||
});
|
||||
|
||||
// --- petitionFormatDate ---
|
||||
|
||||
it('formats a date in Norwegian by default', function () {
|
||||
date_default_timezone_set('UTC');
|
||||
$ctx = mockContext([], 'no');
|
||||
// 2024-03-15 10:05:00 UTC
|
||||
$ts = mktime(10, 5, 0, 3, 15, 2024);
|
||||
expect(petitionFormatDate($ts, $ctx))->toBe('15. mars 2024, 10:05');
|
||||
});
|
||||
|
||||
it('formats a date in English when lang is en', function () {
|
||||
date_default_timezone_set('UTC');
|
||||
$ctx = mockContext([], 'en');
|
||||
$ts = mktime(10, 5, 0, 3, 15, 2024);
|
||||
expect(petitionFormatDate($ts, $ctx))->toBe('March 15, 2024, 10:05');
|
||||
});
|
||||
|
||||
it('uses Norwegian month names for an unknown language', function () {
|
||||
date_default_timezone_set('UTC');
|
||||
$ctx = mockContext([], 'fr');
|
||||
$ts = mktime(0, 0, 0, 1, 1, 2024);
|
||||
$result = petitionFormatDate($ts, $ctx);
|
||||
expect($result)->toContain('januar');
|
||||
});
|
||||
|
||||
it('uses the correct Norwegian month names', function () {
|
||||
date_default_timezone_set('UTC');
|
||||
$ctx = mockContext([], 'no');
|
||||
$months = ['januar','februar','mars','april','mai','juni',
|
||||
'juli','august','september','oktober','november','desember'];
|
||||
foreach ($months as $i => $name) {
|
||||
$ts = mktime(0, 0, 0, $i + 1, 1, 2024);
|
||||
expect(petitionFormatDate($ts, $ctx))->toContain($name);
|
||||
}
|
||||
});
|
||||
|
||||
// --- petitionFormatSignature ---
|
||||
|
||||
it('shows only first name and region for semi display', function () {
|
||||
$ctx = mockContext([
|
||||
'petition.from_region' => 'fra',
|
||||
'regions.oslo' => 'Oslo',
|
||||
]);
|
||||
$sig = ['firstname' => 'Kari', 'surname' => 'Hansen', 'region' => 'oslo', 'display' => 'semi'];
|
||||
$result = petitionFormatSignature($sig, $ctx);
|
||||
expect($result)->toBe('Kari fra Oslo');
|
||||
expect($result)->not->toContain('Hansen');
|
||||
});
|
||||
|
||||
it('shows full name and region for full display', function () {
|
||||
$ctx = mockContext([
|
||||
'petition.from_region' => 'fra',
|
||||
'regions.oslo' => 'Oslo',
|
||||
]);
|
||||
$sig = ['firstname' => 'Kari', 'surname' => 'Hansen', 'region' => 'oslo', 'display' => 'full'];
|
||||
expect(petitionFormatSignature($sig, $ctx))->toBe('Kari Hansen fra Oslo');
|
||||
});
|
||||
|
||||
it('shows anonymous label for anonymous display', function () {
|
||||
$ctx = mockContext([
|
||||
'petition.from_region' => 'fra',
|
||||
'petition.anonymous_name' => 'Anonym',
|
||||
'regions.oslo' => 'Oslo',
|
||||
]);
|
||||
$sig = ['firstname' => 'Kari', 'surname' => 'Hansen', 'region' => 'oslo', 'display' => 'anonymous'];
|
||||
$result = petitionFormatSignature($sig, $ctx);
|
||||
expect($result)->toContain('Anonym');
|
||||
expect($result)->not->toContain('Kari');
|
||||
expect($result)->not->toContain('Hansen');
|
||||
});
|
||||
|
||||
it('HTML-escapes names in full display mode', function () {
|
||||
$ctx = mockContext([
|
||||
'petition.from_region' => 'fra',
|
||||
'regions.oslo' => 'Oslo',
|
||||
]);
|
||||
$sig = ['firstname' => '<b>Kari</b>', 'surname' => '<script>', 'region' => 'oslo', 'display' => 'full'];
|
||||
$result = petitionFormatSignature($sig, $ctx);
|
||||
expect($result)->toContain('<b>');
|
||||
expect($result)->not->toContain('<b>');
|
||||
});
|
||||
|
||||
it('falls back to the region key when no translation exists', function () {
|
||||
$ctx = mockContext(['petition.from_region' => 'fra']);
|
||||
$sig = ['firstname' => 'Ola', 'surname' => 'Nilsen', 'region' => 'trondelag', 'display' => 'semi'];
|
||||
expect(petitionFormatSignature($sig, $ctx))->toContain('trondelag');
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue