innhold/tests/Unit/NewsletterSignupTest.php
Ruben b8e6d2537d 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.
2026-03-17 16:43:39 +01:00

105 lines
3.6 KiB
PHP

<?php
// newsletter-signup.php calls session_start() at load time — pre-initialise
// $_SESSION so the guard fires correctly without emitting headers.
$_SESSION = [];
$_SERVER['REMOTE_ADDR'] ??= '127.0.0.1';
$_SERVER['HTTP_HOST'] ??= 'localhost';
require_once CUSTOM_DIR . '/plugins/page/newsletter-signup.php';
// --- newsletterT ---
it('returns the fallback value when no global context is present', function () {
unset($GLOBALS['ctx']);
expect(newsletterT('name_label'))->toBe('Navn');
expect(newsletterT('submit_button'))->toBe('Meld deg på');
expect(newsletterT('success_message'))->toBe('Sjekk innboksen din!');
});
it('returns the key itself for an unknown key when no context is present', function () {
unset($GLOBALS['ctx']);
expect(newsletterT('nonexistent_key'))->toBe('nonexistent_key');
});
it('returns a translation from context when available', function () {
$GLOBALS['ctx'] = new class {
public function get(string $key, mixed $default = null): mixed
{
return $key === 'translations'
? ['newsletter.name_label' => 'Name']
: $default;
}
};
expect(newsletterT('name_label'))->toBe('Name');
unset($GLOBALS['ctx']);
});
it('falls back to the key when context has no matching translation', function () {
$GLOBALS['ctx'] = new class {
public function get(string $key, mixed $default = null): mixed
{
return $key === 'translations' ? [] : $default;
}
};
expect(newsletterT('name_label'))->toBe('name_label');
unset($GLOBALS['ctx']);
});
// --- newsletterCheckRateLimit ---
it('returns true when no previous submission is recorded in the session', function () {
unset($_SESSION['newsletter_last_submit']);
expect(newsletterCheckRateLimit())->toBeTrue();
});
it('returns false immediately after a submission', function () {
$_SESSION['newsletter_last_submit'] = time();
expect(newsletterCheckRateLimit())->toBeFalse();
});
it('returns false when 29 seconds have elapsed', function () {
$_SESSION['newsletter_last_submit'] = time() - 29;
expect(newsletterCheckRateLimit())->toBeFalse();
});
it('returns true when exactly 30 seconds have elapsed', function () {
$_SESSION['newsletter_last_submit'] = time() - 30;
expect(newsletterCheckRateLimit())->toBeTrue();
});
it('returns true when more than 30 seconds have elapsed', function () {
$_SESSION['newsletter_last_submit'] = time() - 60;
expect(newsletterCheckRateLimit())->toBeTrue();
});
// --- newsletterGetTranslations ---
it('returns all expected translation keys', function () {
unset($GLOBALS['ctx']);
expect(newsletterGetTranslations())->toHaveKeys([
'nameLabel', 'namePlaceholder', 'emailLabel', 'emailPlaceholder',
'notice', 'submitButton', 'successMessage', 'successConfirm', 'errorMessage',
]);
});
it('HTML-escapes special characters in translation values', function () {
$GLOBALS['ctx'] = new class {
public function get(string $key, mixed $default = null): mixed
{
return $key === 'translations'
? ['newsletter.name_label' => '<b>Name & "Label"</b>']
: $default;
}
};
$translations = newsletterGetTranslations();
expect($translations['nameLabel'])->toBe('&lt;b&gt;Name &amp; &quot;Label&quot;&lt;/b&gt;');
unset($GLOBALS['ctx']);
});
it('returns string values for all keys', function () {
unset($GLOBALS['ctx']);
foreach (newsletterGetTranslations() as $key => $value) {
expect($value)->toBeString("Key '{$key}' should be a string");
}
});