Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/Command/ExecuteSuite.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PrestaFlow\Library\Command;

use Error;
use PrestaFlow\Library\Utils\Env;
use PrestaFlow\Library\Utils\Output;
use PrestaFlow\Library\Reports\JUnitReport;
use PrestaFlow\Library\Reports\TestRunSummary;
Expand Down Expand Up @@ -279,11 +280,8 @@ public function execute(InputInterface $input, OutputInterface $output): int
if ($visualPath !== null) {
// Fuseau du stamp : option CLI > env PRESTAFLOW_TZ > UTC. Fallback UTC
// si l'identifiant est invalide (ne casse jamais la génération du rapport).
// On lit à la fois $_ENV et getenv() : selon variables_order de PHP,
// seul l'un ou l'autre peut être peuplé par le shell parent (setup-php CI
// ne peuple pas $_ENV par défaut).
$tzName = $input->getOption('visual-report-tz')
?: ($_ENV['PRESTAFLOW_TZ'] ?? getenv('PRESTAFLOW_TZ') ?: 'UTC');
?: Env::get('PRESTAFLOW_TZ', 'UTC');
try {
$tz = new \DateTimeZone($tzName);
} catch (\Exception $e) {
Expand Down
68 changes: 32 additions & 36 deletions src/Tests/TestsSuite.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use PrestaFlow\Library\Traits\ImportPage;
use PrestaFlow\Library\Traits\Locale;
use PrestaFlow\Library\Traits\Version;
use PrestaFlow\Library\Utils\Env;
use PrestaFlow\Library\Utils\Output;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\ErrorHandler\Error\FatalError;
Expand Down Expand Up @@ -371,16 +372,11 @@ public static function getBrowser(bool $headless = true, bool $force = true)

// Dimensions de la fenêtre : PRESTAFLOW_WINDOW_SIZE_WIDTH/HEIGHT en
// env (utile pour émuler mobile/tablet/desktop). Défaut FHD 1920×1080.
// On lit à la fois $_ENV et getenv() : selon variables_order de PHP,
// seul l'un ou l'autre peut être peuplé par le shell parent (setup-php
// CI ne peuple pas $_ENV par défaut).
$envWidth = $_ENV['PRESTAFLOW_WINDOW_SIZE_WIDTH'] ?? getenv('PRESTAFLOW_WINDOW_SIZE_WIDTH');
$envHeight = $_ENV['PRESTAFLOW_WINDOW_SIZE_HEIGHT'] ?? getenv('PRESTAFLOW_WINDOW_SIZE_HEIGHT');
$winWidth = (int) ($envWidth ?: 1920);
$winHeight = (int) ($envHeight ?: 1080);
$winWidth = (int) (Env::get('PRESTAFLOW_WINDOW_SIZE_WIDTH') ?: 1920);
$winHeight = (int) (Env::get('PRESTAFLOW_WINDOW_SIZE_HEIGHT') ?: 1080);

$options = [
'userAgent' => $_ENV['PRESTAFLOW_USER_AGENT'] ?? getenv('PRESTAFLOW_USER_AGENT') ?: 'PrestaFlow',
'userAgent' => Env::get('PRESTAFLOW_USER_AGENT', 'PrestaFlow'),
'keepAlive' => true,
'windowSize' => [$winWidth, $winHeight],
'headless' => (bool) $headless,
Expand Down Expand Up @@ -508,8 +504,8 @@ public function before($headless = null, bool $getBrowser = true)
*/
protected function presetBasicAuth(): void
{
$user = $_ENV['PRESTAFLOW_BASIC_USER'] ?? null;
$pass = $_ENV['PRESTAFLOW_BASIC_PASS'] ?? null;
$user = Env::get('PRESTAFLOW_BASIC_USER');
$pass = Env::get('PRESTAFLOW_BASIC_PASS');
if ($user === null || $user === '') {
return;
}
Expand Down Expand Up @@ -583,7 +579,7 @@ public static function applyExtraHttpHeaders(): void
*/
protected function presetEnvCookies(): void
{
$raw = $_ENV['PRESTAFLOW_COOKIES'] ?? null;
$raw = Env::get('PRESTAFLOW_COOKIES');
if (!$raw) {
return;
}
Expand Down Expand Up @@ -722,36 +718,36 @@ public function loadGlobals()
$dotenv = Dotenv::createImmutable(__DIR__.'/../../../../../', ['.env.local', '.env']);
$dotenv->safeLoad();

if (isset($_ENV['PRESTAFLOW_DEBUG'])) {
$_ENV['PRESTAFLOW_DEBUG'] = filter_var($_ENV['PRESTAFLOW_DEBUG'], FILTER_VALIDATE_BOOLEAN);
if (Env::has('PRESTAFLOW_DEBUG')) {
$_ENV['PRESTAFLOW_DEBUG'] = filter_var(Env::get('PRESTAFLOW_DEBUG'), FILTER_VALIDATE_BOOLEAN);
} else {
$_ENV['PRESTAFLOW_DEBUG'] = false;
}

if (isset($_ENV['PRESTAFLOW_HEADLESS'])) {
$_ENV['PRESTAFLOW_HEADLESS'] = filter_var($_ENV['PRESTAFLOW_HEADLESS'], FILTER_VALIDATE_BOOLEAN);
if (Env::has('PRESTAFLOW_HEADLESS')) {
$_ENV['PRESTAFLOW_HEADLESS'] = filter_var(Env::get('PRESTAFLOW_HEADLESS'), FILTER_VALIDATE_BOOLEAN);
} else {
$_ENV['PRESTAFLOW_HEADLESS'] = true;
}

if (isset($_ENV['PRESTAFLOW_PREFIX_LOCALE'])) {
$_ENV['PRESTAFLOW_PREFIX_LOCALE'] = filter_var($_ENV['PRESTAFLOW_PREFIX_LOCALE'], FILTER_VALIDATE_BOOLEAN);
if (Env::has('PRESTAFLOW_PREFIX_LOCALE')) {
$_ENV['PRESTAFLOW_PREFIX_LOCALE'] = filter_var(Env::get('PRESTAFLOW_PREFIX_LOCALE'), FILTER_VALIDATE_BOOLEAN);
} else {
$_ENV['PRESTAFLOW_PREFIX_LOCALE'] = false;
}

if (isset($_ENV['PRESTAFLOW_VERBOSE'])) {
$_ENV['PRESTAFLOW_VERBOSE'] = filter_var($_ENV['PRESTAFLOW_VERBOSE'], FILTER_VALIDATE_BOOLEAN);
if (Env::has('PRESTAFLOW_VERBOSE')) {
$_ENV['PRESTAFLOW_VERBOSE'] = filter_var(Env::get('PRESTAFLOW_VERBOSE'), FILTER_VALIDATE_BOOLEAN);
} else {
$_ENV['PRESTAFLOW_VERBOSE'] = true;
}

$frontOfficeUrl = $_ENV['PRESTAFLOW_FO_URL'] ?? 'https://localhost/';
$frontOfficeUrl = Env::get('PRESTAFLOW_FO_URL', 'https://localhost/');
if (!str_ends_with($frontOfficeUrl, '/')) {
$frontOfficeUrl .= '/';
}

$backOfficeUrl = $_ENV['PRESTAFLOW_BO_URL'] ?? $frontOfficeUrl . 'admin-dev/';
$backOfficeUrl = Env::get('PRESTAFLOW_BO_URL', $frontOfficeUrl . 'admin-dev/');
if (!str_starts_with($backOfficeUrl, 'https://') && !str_starts_with($backOfficeUrl, 'http://')) {
$backOfficeUrl = $frontOfficeUrl . $backOfficeUrl;
}
Expand All @@ -760,31 +756,31 @@ public function loadGlobals()
}

$this->globals = [
'PS_VERSION' => $_ENV['PRESTAFLOW_PS_VERSION'] ?? '8.1.0',
'LOCALE' => $_ENV['PRESTAFLOW_LOCALE'] ?? 'en',
'PREFIX_LOCALE' => (bool) $_ENV['PRESTAFLOW_PREFIX_LOCALE'] ?? false,
'PS_VERSION' => Env::get('PRESTAFLOW_PS_VERSION', '8.1.0'),
'LOCALE' => Env::get('PRESTAFLOW_LOCALE', 'en'),
'PREFIX_LOCALE' => (bool) Env::get('PRESTAFLOW_PREFIX_LOCALE', false),
'BO' => [
'URL' => $backOfficeUrl,
'EMAIL' => $_ENV['PRESTAFLOW_BO_EMAIL'] ?? 'demo@prestashop.com',
'PASSWD' => $_ENV['PRESTAFLOW_BO_PASSWD'] ?? 'Correct Horse Battery Staple',
'EMAIL' => Env::get('PRESTAFLOW_BO_EMAIL', 'demo@prestashop.com'),
'PASSWD' => Env::get('PRESTAFLOW_BO_PASSWD', 'Correct Horse Battery Staple'),
],
'FO' => [
'URL' => $frontOfficeUrl,
'EMAIL' => $_ENV['PRESTAFLOW_FO_EMAIL'] ?? 'pub@prestashop.com',
'PASSWD' => $_ENV['PRESTAFLOW_FO_PASSWD'] ?? '123456789',
'EMAIL' => Env::get('PRESTAFLOW_FO_EMAIL', 'pub@prestashop.com'),
'PASSWD' => Env::get('PRESTAFLOW_FO_PASSWD', '123456789'),
],
'DEBUG' => (bool) $_ENV['PRESTAFLOW_DEBUG'] ?? false,
'VERBOSE' => (bool) $_ENV['PRESTAFLOW_VERBOSE'] ?? true,
'DEBUG' => (bool) Env::get('PRESTAFLOW_DEBUG', false),
'VERBOSE' => (bool) Env::get('PRESTAFLOW_VERBOSE', true),
'BROWSER' => [
'HEADLESS' => (bool) $_ENV['PRESTAFLOW_HEADLESS'] ?? true,
'WINDOW_SIZE_HEIGHT' => $_ENV['PRESTAFLOW_WINDOW_SIZE_HEIGHT'] ?? 1920,
'WINDOW_SIZE_WIDTH' => $_ENV['PRESTAFLOW_WINDOW_SIZE_WIDTH'] ?? 1000,
'USER_AGENT' => $_ENV['PRESTAFLOW_USER_AGENT'] ?? 'PrestaFlow',
'HEADLESS' => (bool) Env::get('PRESTAFLOW_HEADLESS', true),
'WINDOW_SIZE_HEIGHT' => Env::get('PRESTAFLOW_WINDOW_SIZE_HEIGHT', 1920),
'WINDOW_SIZE_WIDTH' => Env::get('PRESTAFLOW_WINDOW_SIZE_WIDTH', 1000),
'USER_AGENT' => Env::get('PRESTAFLOW_USER_AGENT', 'PrestaFlow'),
],
];

$this->exctractVersions($_ENV['PRESTAFLOW_PS_VERSION'] ?? '8.1.0');
$this->setLocale($_ENV['PRESTAFLOW_LOCALE'] ?? 'en');
$this->exctractVersions(Env::get('PRESTAFLOW_PS_VERSION', '8.1.0'));
$this->setLocale(Env::get('PRESTAFLOW_LOCALE', 'en'));
}

public function isVerboseMode(): bool
Expand Down
4 changes: 3 additions & 1 deletion src/Traits/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace PrestaFlow\Library\Traits;

use PrestaFlow\Library\Utils\Env;

trait Version
{
const SUPPORTED_VERSIONS = [
Expand Down Expand Up @@ -48,7 +50,7 @@ public function resolveVersion(): void

$version = $this->psVersionOverride
?? $propertyVersion
?? ($_ENV['PRESTAFLOW_PS_VERSION'] ?? null)
?? Env::get('PRESTAFLOW_PS_VERSION')
?? ($this->globals['PS_VERSION'] ?? null)
?? '8.1.0';

Expand Down
48 changes: 48 additions & 0 deletions src/Utils/Env.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace PrestaFlow\Library\Utils;

/**
* Read process env vars in a way that survives PHP's variables_order setting.
*
* The library historically read env vars via $_ENV['KEY'], relying on phpdotenv
* to populate that superglobal from .env / .env.local files. When PHP is built
* with a variables_order that omits 'E' (the common CI case), plain process env
* vars set by the shell — e.g. GitHub Actions' env: block, or `KEY=v php ...`
* on a dev machine — never make it into $_ENV, so those callsites silently
* fell back to defaults.
*
* Env::get() looks at $_ENV first (preserving the dotenv-populated,
* normalized values that TestsSuite writes at load time) and falls back to
* getenv() (which always sees the process environment).
*/
class Env
{
/**
* Read an env var: $_ENV first, then getenv(), then the default.
*
* Semantics match the previous `$_ENV[$key] ?? $default` idiom:
* - key present in $_ENV → returns that value verbatim (even empty string)
* - key absent from $_ENV but present in process env → returns that
* - key absent from both → returns $default
*/
public static function get(string $key, mixed $default = null): mixed
{
if (array_key_exists($key, $_ENV)) {
return $_ENV[$key];
}
$val = getenv($key);
return $val !== false ? $val : $default;
}

/**
* True if the key exists in $_ENV or in the process environment.
*/
public static function has(string $key): bool
{
if (array_key_exists($key, $_ENV)) {
return true;
}
return getenv($key) !== false;
}
}
4 changes: 3 additions & 1 deletion src/Utils/Screenshots.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace PrestaFlow\Library\Utils;

use PrestaFlow\Library\Utils\Env;

final class Screenshots
{
public const ERRORS_SUBPATH = 'screens/errors';
Expand Down Expand Up @@ -94,7 +96,7 @@ private static function visualPath(string $subpath, string $fileName, bool $crea

public static function captureDelay(): int
{
$delay = (int) ($_ENV['PRESTAFLOW_SCREENSHOT_DELAY'] ?? 3);
$delay = (int) Env::get('PRESTAFLOW_SCREENSHOT_DELAY', 3);

return $delay < 0 ? 0 : $delay;
}
Expand Down
83 changes: 83 additions & 0 deletions tests/Unit/Utils/EnvTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace PrestaFlow\Tests\Unit\Utils;

use PHPUnit\Framework\TestCase;
use PrestaFlow\Library\Utils\Env;

final class EnvTest extends TestCase
{
private array $envBackup;

protected function setUp(): void
{
// Snapshot only the keys we touch, plus track anything we set.
$this->envBackup = [];
foreach (['PF_TEST_A', 'PF_TEST_B', 'PF_TEST_EMPTY', 'PF_TEST_MISSING'] as $k) {
$this->envBackup[$k] = [
'env' => array_key_exists($k, $_ENV) ? $_ENV[$k] : null,
'envHas' => array_key_exists($k, $_ENV),
'getenv' => getenv($k),
];
unset($_ENV[$k]);
putenv($k);
}
}

protected function tearDown(): void
{
foreach ($this->envBackup as $k => $snap) {
unset($_ENV[$k]);
putenv($k);
if ($snap['envHas']) {
$_ENV[$k] = $snap['env'];
}
if ($snap['getenv'] !== false) {
putenv("$k={$snap['getenv']}");
}
}
}

public function testGetReturnsDefaultWhenAbsentEverywhere(): void
{
$this->assertNull(Env::get('PF_TEST_MISSING'));
$this->assertSame('fallback', Env::get('PF_TEST_MISSING', 'fallback'));
}

public function testGetReadsFromEnvSuperglobalFirst(): void
{
$_ENV['PF_TEST_A'] = 'from-env-superglobal';
putenv('PF_TEST_A=from-process');
$this->assertSame('from-env-superglobal', Env::get('PF_TEST_A'));
}

public function testGetFallsBackToGetenvWhenSuperglobalAbsent(): void
{
putenv('PF_TEST_B=from-process-only');
$this->assertSame('from-process-only', Env::get('PF_TEST_B'));
}

public function testGetReturnsEmptyStringFromSuperglobalVerbatim(): void
{
// Preserves `?? $default` semantics: a key set to '' still wins over the default.
$_ENV['PF_TEST_EMPTY'] = '';
$this->assertSame('', Env::get('PF_TEST_EMPTY', 'default'));
}

public function testHasTrueWhenInSuperglobal(): void
{
$_ENV['PF_TEST_A'] = 'x';
$this->assertTrue(Env::has('PF_TEST_A'));
}

public function testHasTrueWhenInProcessOnly(): void
{
putenv('PF_TEST_B=x');
$this->assertTrue(Env::has('PF_TEST_B'));
}

public function testHasFalseWhenAbsentEverywhere(): void
{
$this->assertFalse(Env::has('PF_TEST_MISSING'));
}
}