From 1af30e0a255951308a202e0afed709cdbb44471d Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Fri, 25 May 2018 10:26:14 -0500 Subject: [PATCH] MQE-987: Decouple MFTF from Magento - add new mftf console commands - include new bootstrap from mftf autoloading - update tests and pathing to be windows compatible --- RoboFile.php | 214 ------------------ bin/mftf | 16 +- bootstrap.php | 8 - composer.json | 1 + dev/tests/_bootstrap.php | 10 +- dev/tests/functional/MFTF/_bootstrap.php | 7 - dev/tests/functional/_bootstrap.php | 31 ++- .../MFTF}/DevDocs/Page/MFTFDocPage.xml | 0 .../MFTF}/DevDocs/Section/ContentSection.xml | 0 .../MFTF}/DevDocs/Test/DevDocsTest.xml | 0 .../Suite/SuiteGeneratorTest.php | 12 +- .../Tests/SuiteGenerationTest.php | 55 ++--- .env.example => etc/config/.env.example | 0 .../config/codeception.dist.yml | 10 +- .../config/functional.suite.dist.yml | 7 +- .../Config/FileResolver/Root.php | 4 +- .../Console/BuildProjectCommand.php | 81 +++++-- .../Console/CleanProjectCommand.php | 92 ++++++++ .../Console/GenerateSuiteCommand.php | 56 +++++ .../Console/GenerateTestsCommand.php | 140 ++++++++++++ .../Console/RunTestCommand.php | 73 ++++++ .../Console/RunTestGroupCommand.php | 104 +++++++++ .../Console/SetupEnvCommand.php | 8 +- .../Suite/SuiteGenerator.php | 5 +- .../Util/Env/EnvProcessor.php | 49 +++- .../Util/Manifest/BaseTestManifest.php | 3 +- .../Util/ModuleResolver.php | 1 + .../FunctionalTestingFramework/_bootstrap.php | 63 ++++++ 28 files changed, 715 insertions(+), 335 deletions(-) delete mode 100644 RoboFile.php delete mode 100644 bootstrap.php delete mode 100755 dev/tests/functional/MFTF/_bootstrap.php rename dev/tests/functional/{MFTF/FunctionalTest => tests/MFTF}/DevDocs/Page/MFTFDocPage.xml (100%) rename dev/tests/functional/{MFTF/FunctionalTest => tests/MFTF}/DevDocs/Section/ContentSection.xml (100%) rename dev/tests/functional/{MFTF/FunctionalTest => tests/MFTF}/DevDocs/Test/DevDocsTest.xml (100%) rename .env.example => etc/config/.env.example (100%) rename codeception.dist.yml => etc/config/codeception.dist.yml (72%) rename dev/tests/functional/MFTF.suite.dist.yml => etc/config/functional.suite.dist.yml (84%) create mode 100644 src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php create mode 100644 src/Magento/FunctionalTestingFramework/_bootstrap.php diff --git a/RoboFile.php b/RoboFile.php deleted file mode 100644 index c0af4c72c..000000000 --- a/RoboFile.php +++ /dev/null @@ -1,214 +0,0 @@ -_exec('cp -vn .env.example .env'); - $this->_exec('cp -vf codeception.dist.yml codeception.yml'); - $this->_exec('cp -vf dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR .'MFTF.suite.dist.yml dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR .'MFTF.suite.yml'); - } - - /** - * Duplicate the Example configuration files for the Project. - * Build the Codeception project. - * - * @return void - */ - function buildProject() - { - $this->writeln("This command will be removed in MFTF v3.0.0. Please use bin/mftf build:project instead.\n"); - $this->cloneFiles(); - $this->_exec('vendor'. DIRECTORY_SEPARATOR .'bin'. DIRECTORY_SEPARATOR .'codecept build'); - } - - /** - * Generate all Tests in PHP. - * - * @param array $tests - * @param array $opts - * @return void - */ - function generateTests(array $tests, $opts = ['config' => null, 'force' => true, 'nodes' => null, 'debug' => false]) - { - require 'dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php'; - $GLOBALS['GENERATE_TESTS'] = true; - if (!$this->isProjectBuilt()) { - $this->say("Please run bin/mftf build:project and configure your environment (.env) first."); - exit(\Robo\Result::EXITCODE_ERROR); - } - $testsObjects = []; - foreach ($tests as $test) { - $testsObjects[] = Magento\FunctionalTestingFramework\Test\Handlers\TestObjectHandler::getInstance()->getObject($test); - } - if ($opts['force']) { - $GLOBALS['FORCE_PHP_GENERATE'] = true; - } - $testsReferencedInSuites = \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance()->generateAllSuites($opts['config']); - \Magento\FunctionalTestingFramework\Util\TestGenerator::getInstance(null, $testsObjects, $opts['debug'])->createAllTestFiles($opts['config'], $opts['nodes'], $testsReferencedInSuites); - $this->say("Generate Tests Command Run"); - } - - /** - * Check if MFTF has been properly configured - * @return bool - */ - private function isProjectBuilt() - { - $actorFile = __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Magento' . DIRECTORY_SEPARATOR . 'FunctionalTestingFramework' . DIRECTORY_SEPARATOR . '_generated' . DIRECTORY_SEPARATOR . 'AcceptanceTesterActions.php'; - - $login = !empty(getenv('MAGENTO_ADMIN_USERNAME')); - $password = !empty(getenv('MAGENTO_ADMIN_PASSWORD')); - $baseUrl = !empty(getenv('MAGENTO_BASE_URL')); - $backendName = !empty(getenv('MAGENTO_BACKEND_NAME')); - $test = (file_exists($actorFile) && $login && $password && $baseUrl && $backendName); - return $test; - } - - /** - * Generate a suite based on name(s) passed in as args. - * - * @param array $args - * @throws Exception - * @return void - */ - function generateSuite(array $args) - { - if (empty($args)) { - throw new Exception("Please provide suite name(s) after generate:suite command"); - } - - require 'dev' . DIRECTORY_SEPARATOR . 'tests'. DIRECTORY_SEPARATOR . 'functional' . DIRECTORY_SEPARATOR . '_bootstrap.php'; - $sg = \Magento\FunctionalTestingFramework\Suite\SuiteGenerator::getInstance(); - - foreach ($args as $arg) { - $sg->generateSuite($arg); - } - } - - /** - * Run all MFTF tests. - * - * @return void - */ - function mftf() - { - $this->_exec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run MFTF --skip-group skip'); - } - - /** - * Run all Tests with the specified @group tag, excluding @group 'skip'. - * - * @param string $args - * @return void - */ - function group($args = '') - { - $this->taskExec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run --verbose --steps --skip-group skip --group')->args($args)->run(); - } - - /** - * Run all Functional tests located under the Directory Path provided. - * - * @param string $args - * @return void - */ - function folder($args = '') - { - $this->taskExec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run ')->args($args)->run(); - } - - /** - * Run all Tests marked with the @group tag 'example'. - * - * @return void - */ - function example() - { - $this->_exec('.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'codecept run --group example --skip-group skip'); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v1.4.X - * - * @return \Robo\Result - */ - function allure1Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' -o tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v2.3.X - * - * @return \Robo\Result - */ - function allure2Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' --output tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .' --clean'); - } - - /** - * Open the HTML Allure report - Allure v1.4.X - * - * @return void - */ - function allure1Open() - { - $this->_exec('allure report open --report-dir tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Open the HTML Allure report - Allure v2.3.X - * - * @return void - */ - function allure2Open() - { - $this->_exec('allure open --port 0 tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate and open the HTML Allure report - Allure v1.4.X - * - * @return void - */ - function allure1Report() - { - $result1 = $this->allure1Generate(); - - if ($result1->wasSuccessful()) { - $this->allure1Open(); - } - } - - /** - * Generate and open the HTML Allure report - Allure v2.3.X - * - * @return void - */ - function allure2Report() - { - $result1 = $this->allure2Generate(); - - if ($result1->wasSuccessful()) { - $this->allure2Open(); - } - } -} diff --git a/bin/mftf b/bin/mftf index 623e3bc61..b8c1cce80 100755 --- a/bin/mftf +++ b/bin/mftf @@ -11,13 +11,27 @@ if (PHP_SAPI !== 'cli') { exit(1); } +$autoload_path = realpath(__DIR__ . '/../../../autoload.php'); +$test_bootstrap_path = realpath(__DIR__ . '/../dev/tests/functional/_bootstrap.php'); + +if (file_exists($autoload_path)) { + require_once $autoload_path; +} else { + require_once $test_bootstrap_path; +} + + try { - require_once __DIR__ . '/../bootstrap.php'; $application = new Symfony\Component\Console\Application(); $application->setName('Magento Functional Testing Framework CLI'); $application->setVersion('1.0.0'); $application->add(new Magento\FunctionalTestingFramework\Console\SetupEnvCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\CleanProjectCommand()); $application->add(new Magento\FunctionalTestingFramework\Console\BuildProjectCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\GenerateSuiteCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\GenerateTestsCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\RunTestGroupCommand()); + $application->add(new Magento\FunctionalTestingFramework\Console\RunTestCommand()); $application->run(); } catch (\Exception $e) { while ($e) { diff --git a/bootstrap.php b/bootstrap.php deleted file mode 100644 index 7a708e8a8..000000000 --- a/bootstrap.php +++ /dev/null @@ -1,8 +0,0 @@ -init([ 'debug' => true, - 'includePaths' => [PROJECT_ROOT . '/src'], + 'includePaths' => [PROJECT_ROOT . DIRECTORY_SEPARATOR . 'src'], 'cacheDir' => PROJECT_ROOT . DIRECTORY_SEPARATOR . 'dev' . diff --git a/dev/tests/functional/MFTF/_bootstrap.php b/dev/tests/functional/MFTF/_bootstrap.php deleted file mode 100755 index 6a524e867..000000000 --- a/dev/tests/functional/MFTF/_bootstrap.php +++ /dev/null @@ -1,7 +0,0 @@ -load(); - - if (array_key_exists('TESTS_MODULE_PATH', $_ENV) xor array_key_exists('TESTS_BP', $_ENV)) { - throw new Exception('You must define both parameters TESTS_BP and TESTS_MODULE_PATH or neither parameter'); - } - - foreach ($_ENV as $key => $var) { - defined($key) || define($key, $var); - } -} defined('FW_BP') || define('FW_BP', PROJECT_ROOT); // add the debug flag here @@ -29,7 +16,17 @@ xdebug_disable(); } -$RELATIVE_TESTS_MODULE_PATH = '/MFTF/FunctionalTest'; +$RELATIVE_TESTS_MODULE_PATH = '/tests/functional/tests/MFTF'; -defined('TESTS_BP') || define('TESTS_BP', __DIR__); -defined('TESTS_MODULE_PATH') || define('TESTS_MODULE_PATH', TESTS_BP . $RELATIVE_TESTS_MODULE_PATH); +defined('MAGENTO_BP') || define('MAGENTO_BP', PROJECT_ROOT); +defined('TESTS_BP') || define('TESTS_BP', dirname(dirname(__DIR__))); +defined('TESTS_MODULE_PATH') || define('TESTS_MODULE_PATH', realpath(TESTS_BP . $RELATIVE_TESTS_MODULE_PATH)); + +if (file_exists(TESTS_BP . DIRECTORY_SEPARATOR . '.env')) { + $env = new \Dotenv\Loader(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); + $env->load(); + + foreach ($_ENV as $key => $var) { + defined($key) || define($key, $var); + } +} diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Page/MFTFDocPage.xml b/dev/tests/functional/tests/MFTF/DevDocs/Page/MFTFDocPage.xml similarity index 100% rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Page/MFTFDocPage.xml rename to dev/tests/functional/tests/MFTF/DevDocs/Page/MFTFDocPage.xml diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Section/ContentSection.xml b/dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml similarity index 100% rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Section/ContentSection.xml rename to dev/tests/functional/tests/MFTF/DevDocs/Section/ContentSection.xml diff --git a/dev/tests/functional/MFTF/FunctionalTest/DevDocs/Test/DevDocsTest.xml b/dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml similarity index 100% rename from dev/tests/functional/MFTF/FunctionalTest/DevDocs/Test/DevDocsTest.xml rename to dev/tests/functional/tests/MFTF/DevDocs/Test/DevDocsTest.xml diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php index 8eb486144..d6790f00e 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Suite/SuiteGeneratorTest.php @@ -23,6 +23,17 @@ class SuiteGeneratorTest extends TestCase { + /** + * Setup entry append and clear for Suite Generator + */ + public static function setUpBeforeClass() + { + AspectMock::double(SuiteGenerator::class, [ + 'clearPreviousSessionConfigEntries' => null, + 'appendEntriesToConfig' => null + ]); + } + /** * Tests generating a single suite given a set of parsed test data * @throws \Exception @@ -169,6 +180,5 @@ private function setMockTestAndSuiteParserOutput($testData, $suiteData) $property = new \ReflectionProperty(SuiteGenerator::class, 'groupClassGenerator'); $property->setAccessible(true); $property->setValue($instance, $instance); - } } diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index 5f2d3a6d2..4024329f1 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -8,15 +8,17 @@ use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; +use Magento\FunctionalTestingFramework\Util\Manifest\DefaultTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\ParallelTestManifest; use Magento\FunctionalTestingFramework\Util\Manifest\TestManifestFactory; +use PHPUnit\Util\Filesystem; use Symfony\Component\Yaml\Yaml; use tests\util\MftfTestCase; class SuiteGenerationTest extends MftfTestCase { const RESOURCES_DIR = TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'Resources'; - const CONFIG_YML_FILE = FW_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; + const CONFIG_YML_FILE = TESTS_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; const GENERATE_RESULT_DIR = TESTS_BP . DIRECTORY_SEPARATOR . "verification" . @@ -24,13 +26,6 @@ class SuiteGenerationTest extends MftfTestCase "_generated" . DIRECTORY_SEPARATOR; - /** - * Flag to track existence of config.yml file - * - * @var bool - */ - private static $YML_EXISTS_FLAG = false; - /** * Array which stores state of any existing config.yml groups * @@ -43,18 +38,20 @@ class SuiteGenerationTest extends MftfTestCase */ public static function setUpBeforeClass() { - if (file_exists(self::CONFIG_YML_FILE)) { - self::$YML_EXISTS_FLAG = true; - return; - } - // destroy _generated if it exists if (file_exists(self::GENERATE_RESULT_DIR)) { DirSetupUtil::rmdirRecursive(self::GENERATE_RESULT_DIR); } + } - $configYml = fopen(self::CONFIG_YML_FILE, "w"); - fclose($configYml); + public function setUp() + { + // copy config yml file to test dir + $fileSystem = new \Symfony\Component\Filesystem\Filesystem(); + $fileSystem->copy( + realpath(FW_BP . '/etc/config/codeception.dist.yml'), + self::CONFIG_YML_FILE + ); } /** @@ -200,7 +197,6 @@ public function testSuiteGenerationHooks() self::RESOURCES_PATH . DIRECTORY_SEPARATOR . $groupName . ".txt", $groupFile ); - } /** @@ -219,7 +215,7 @@ public function testSuiteGenerationSingleRun() ]; //createParallelManifest - /** @var ParallelTestManifest $parallelManifest */ + /** @var DefaultTestManifest $parallelManifest */ $singleRunManifest = TestManifestFactory::makeManifest("singleRun", ["functionalSuite2" => []]); // Generate the Suite @@ -246,7 +242,7 @@ public function testSuiteGenerationSingleRun() $this->assertTrue(in_array($expectedFile, $dirContents)); } - $expectedManifest = "dev/tests/verification/_generated/default/" . PHP_EOL . "-g functionalSuite2" . PHP_EOL; + $expectedManifest = "verification/_generated/default/" . PHP_EOL . "-g functionalSuite2" . PHP_EOL; $this->assertEquals($expectedManifest, file_get_contents(self::getManifestFilePath())); } @@ -257,26 +253,13 @@ public function testSuiteGenerationSingleRun() */ public function tearDown() { - // restore config if we see there was an original codeception.yml file - if (self::$YML_EXISTS_FLAG) { - $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); - foreach (self::$TEST_GROUPS as $testGroup) { - unset($yml['groups'][$testGroup]); - } - - file_put_contents(self::CONFIG_YML_FILE, Yaml::dump($yml, 10)); - } DirSetupUtil::rmdirRecursive(self::GENERATE_RESULT_DIR); - } - /** - * Remove yml if created during tests and did not exist before - */ - public static function tearDownAfterClass() - { - if (!self::$YML_EXISTS_FLAG) { - unlink(self::CONFIG_YML_FILE); - } + // delete config yml file from test dir + $fileSystem = new \Symfony\Component\Filesystem\Filesystem(); + $fileSystem->remove( + self::CONFIG_YML_FILE + ); } /** diff --git a/.env.example b/etc/config/.env.example similarity index 100% rename from .env.example rename to etc/config/.env.example diff --git a/codeception.dist.yml b/etc/config/codeception.dist.yml similarity index 72% rename from codeception.dist.yml rename to etc/config/codeception.dist.yml index 25093c681..273ede0b0 100755 --- a/codeception.dist.yml +++ b/etc/config/codeception.dist.yml @@ -2,19 +2,19 @@ # See COPYING.txt for license details. actor: Tester paths: - tests: dev/tests/functional - log: dev/tests/functional/_output - data: dev/tests/functional/_data + tests: tests + log: tests/_output + data: tests/_data support: src/Magento/FunctionalTestingFramework envs: etc/_envs settings: - bootstrap: _bootstrap.php colors: true memory_limit: 1024M extensions: enabled: - Codeception\Extension\RunFailed - - Yandex\Allure\Adapter\AllureAdapter + - Magento\FunctionalTestingFramework\Extension\TestContextExtension + - Magento\FunctionalTestingFramework\Allure\Adapter\MagentoAllureAdapter config: Yandex\Allure\Adapter\AllureAdapter: deletePreviousResults: true diff --git a/dev/tests/functional/MFTF.suite.dist.yml b/etc/config/functional.suite.dist.yml similarity index 84% rename from dev/tests/functional/MFTF.suite.dist.yml rename to etc/config/functional.suite.dist.yml index a54620385..bfd093e41 100644 --- a/dev/tests/functional/MFTF.suite.dist.yml +++ b/etc/config/functional.suite.dist.yml @@ -14,14 +14,15 @@ modules: - \Magento\FunctionalTestingFramework\Module\MagentoWebDriver - \Magento\FunctionalTestingFramework\Helper\Acceptance - \Magento\FunctionalTestingFramework\Helper\MagentoFakerData + - \Magento\FunctionalTestingFramework\Module\MagentoSequence - \Magento\FunctionalTestingFramework\Module\MagentoAssert - Asserts config: \Magento\FunctionalTestingFramework\Module\MagentoWebDriver: url: "%MAGENTO_BASE_URL%" backend_name: "%MAGENTO_BACKEND_NAME%" - browser: '%BROWSER%' - window_size: maximize + browser: 'chrome' + window_size: 1280x1024 username: "%MAGENTO_ADMIN_USERNAME%" password: "%MAGENTO_ADMIN_PASSWORD%" pageload_timeout: 30 @@ -31,5 +32,5 @@ modules: path: %SELENIUM_PATH% capabilities: chromeOptions: - args: ["--start-maximized", "--disable-extensions", "--enable-automation"] + args: ["--incognito", "--disable-extensions", "--enable-automation"] diff --git a/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php index 93e74a82c..3b0940b28 100644 --- a/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php +++ b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php @@ -11,7 +11,7 @@ class Root extends Module { - const ROOT_SUITE_DIR = "_suite"; + const ROOT_SUITE_DIR = "tests/_suite"; /** * Retrieve the list of configuration files with given name that relate to specified scope at the root level as well @@ -25,7 +25,7 @@ public function get($filename, $scope) { // first pick up the root level test suite dir $paths = glob( - dirname(TESTS_BP) . DIRECTORY_SEPARATOR . self::ROOT_SUITE_DIR + TESTS_BP . DIRECTORY_SEPARATOR . self::ROOT_SUITE_DIR . DIRECTORY_SEPARATOR . $filename ); diff --git a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php index 6e29ac071..ea5c5795f 100644 --- a/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/BuildProjectCommand.php @@ -16,9 +16,12 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Process; use Magento\FunctionalTestingFramework\Util\Env\EnvProcessor; +use Symfony\Component\Yaml\Yaml; class BuildProjectCommand extends Command { + const DEFAULT_YAML_INLINE_DEPTH = 10; + /** * Env processor manages .env files. * @@ -33,9 +36,9 @@ class BuildProjectCommand extends Command */ protected function configure() { - $this->setName('build:project'); - $this->setDescription('Generate configuration files for the project. Build the Codeception project.'); - $this->envProcessor = new EnvProcessor(BP . DIRECTORY_SEPARATOR . '.env'); + $this->setName('build:project') + ->setDescription('Generate configuration files for the project. Build the Codeception project.'); + $this->envProcessor = new EnvProcessor(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); $env = $this->envProcessor->getEnv(); foreach ($env as $key => $value) { $this->addOption($key, null, InputOption::VALUE_REQUIRED, '', $value); @@ -49,22 +52,12 @@ protected function configure() * @param OutputInterface $output * @return void * @throws \Symfony\Component\Console\Exception\LogicException + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function execute(InputInterface $input, OutputInterface $output) { - $fileSystem = new Filesystem(); - $fileSystem->copy( - BP . DIRECTORY_SEPARATOR . 'codeception.dist.yml', - BP . DIRECTORY_SEPARATOR . 'codeception.yml' - ); - $output->writeln("codeception.yml configuration successfully applied.\n"); - $fileSystem->copy( - BP . DIRECTORY_SEPARATOR . 'dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . - 'functional' . DIRECTORY_SEPARATOR . 'MFTF.suite.dist.yml', - BP . DIRECTORY_SEPARATOR . 'dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . - 'functional' . DIRECTORY_SEPARATOR . 'MFTF.suite.yml' - ); - $output->writeln("MFTF.suite.yml configuration successfully applied.\n"); + $this->generateConfigFiles($output); $setupEnvCommand = new SetupEnvCommand(); $commandInput = []; @@ -78,12 +71,58 @@ protected function execute(InputInterface $input, OutputInterface $output) $commandInput = new ArrayInput($commandInput); $setupEnvCommand->run($commandInput, $output); - $process = new Process('vendor/bin/codecept build'); - $process->run(); - if ($process->isSuccessful()) { - $output->writeln("Codeception build run successfully.\n"); + + // TODO can we just import the codecept symfony command? + $codeceptBuildCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' build'; + $process = new Process($codeceptBuildCommand); + $process->setWorkingDirectory(TESTS_BP); + $process->run( + function ($type, $buffer) use ($output) { + if ($output->isVerbose()) { + $output->write($buffer); + } + } + ); + } + + /** + * Generates needed codeception configuration files to the TEST_BP directory + * + * @param OutputInterface $output + * @return void + */ + private function generateConfigFiles(OutputInterface $output) + { + $fileSystem = new Filesystem(); + //Find travel path from codeception.yml to FW_BP + $relativePath = $fileSystem->makePathRelative(FW_BP, TESTS_BP); + + if (!$fileSystem->exists(TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml')) { + // read in the codeception.yml file + $configDistYml = Yaml::parse(file_get_contents(realpath(FW_BP . "/etc/config/codeception.dist.yml"))); + $configDistYml['paths']['support'] = $relativePath . 'src/Magento/FunctionalTestingFramework'; + $configDistYml['paths']['envs'] = $relativePath . 'etc/_envs'; + $configYmlText = Yaml::dump($configDistYml, self::DEFAULT_YAML_INLINE_DEPTH); + + // dump output to new codeception.yml file + file_put_contents(TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml', $configYmlText); + $output->writeln("codeception.yml configuration successfully applied."); } - $output->writeln('The project built successfully.'); + if ($output->isVerbose()) { + $output->writeln("codeception.yml applied to " . TESTS_BP . DIRECTORY_SEPARATOR . 'codeception.yml'); + } + + // copy the functional suite yml, this will only copy if there are differences between the template the destination + $fileSystem->copy( + realpath(FW_BP . '/etc/config/functional.suite.dist.yml'), + TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml' + ); + $output->writeln('functional.suite.yml configuration successfully applied.'); + + if ($output->isVerbose()) { + $output->writeln("functional.suite.yml applied to " . + TESTS_BP . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'functional.suite.yml'); + } } } diff --git a/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php new file mode 100644 index 000000000..c2d388898 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/CleanProjectCommand.php @@ -0,0 +1,92 @@ +setName('reset') + ->setDescription('This command will clean any configuration files from the environment (not including .env), as well as any generated artifacts.') + ->addOption('hard', null, InputOption::VALUE_NONE, "parameter to force reset of configuration files."); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $isHardReset = $input->getOption('hard'); + $fileSystem = new Filesystem(); + $finder = new Finder(); + $finder->files()->name('*.php')->in(realpath(FW_BP . '/src/Magento/FunctionalTestingFramework/Group/')); + $filesForRemoval = []; + + // include config files if user specifies a hard reset for deletion + if ($isHardReset) { + $filesForRemoval = array_merge($filesForRemoval, self::CONFIGURATION_FILES); + } + + // include the files mftf generates during test execution in TESTS_BP for deletion + $filesForRemoval = array_merge($filesForRemoval, self::GENERATED_FILES); + + if ($output->isVerbose()) { + $output->writeln('Deleting Files:'); + } + + // delete any suite based group files + foreach ($finder->files() as $file) { + if ($output->isVerbose()) { + $output->writeln($file->getRealPath()); + } + + $fileSystem->remove($file); + } + + // delete files specified for removal + foreach ($filesForRemoval as $fileForRemoval) { + if ($fileSystem->exists($fileForRemoval) && $output->isVerbose()) { + $output->writeln($fileForRemoval); + } + + $fileSystem->remove($fileForRemoval); + } + + $output->writeln('mftf files removed from filesystem.'); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php new file mode 100644 index 000000000..d94e5fa2e --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateSuiteCommand.php @@ -0,0 +1,56 @@ +setName('generate:suite') + ->setDescription('This command generates a single suite based on declaration in xml') + ->addArgument( + 'suites', + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + 'argument which indicates suite names for generation (separated by space)' + ); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $suites = $input->getArgument('suites'); + + foreach ($suites as $suite) { + SuiteGenerator::getInstance()->generateSuite($suite); + if ($output->isVerbose()) { + $output->writeLn("suite $suite generated"); + } + } + + $output->writeLn("Suites Generated"); + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php new file mode 100644 index 000000000..a8d34f1c9 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/GenerateTestsCommand.php @@ -0,0 +1,140 @@ +setName('generate:tests') + ->setDescription('This command generates all test files and suites based on xml declarations') + ->addArgument('name', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'name(s) of specific tests to generate') + ->addOption("config", 'c', InputOption::VALUE_REQUIRED, 'default, singleRun, or parallel', 'default') + ->addOption("force", 'f',InputOption::VALUE_NONE, 'force generation of tests regardless of Magento Instance Configuration') + ->addOption('lines', 'l', InputOption::VALUE_REQUIRED, 'Used in combination with a parallel configuration, determines desired group size', 500) + ->addOption('tests', 't', InputOption::VALUE_REQUIRED, 'A parameter accepting a JSON string used to determine the test configuration'); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $tests = $input->getArgument('name'); + $config = $input->getOption('config'); + $json = $input->getOption('tests'); + $force = $input->getOption('force'); + $lines = $input->getOption('lines'); + $verbose = $output->isVerbose(); + + if ($json !== null && !json_decode($json)) { + // stop execution if we have failed to properly parse any json passed in by the user + throw new TestFrameworkException("JSON could not be parsed: " . json_last_error_msg()); + } + + $testConfiguration = $this->createTestConfiguration($json, $tests, $force, $verbose); + + // create our manifest file here + $testManifest = TestManifestFactory::makeManifest($config, $testConfiguration['suites']); + TestGenerator::getInstance(null, $testConfiguration['tests'])->createAllTestFiles($testManifest); + + if ($config == 'parallel') { + /** @var ParallelTestManifest $testManifest */ + $testManifest->createTestGroups($lines); + } + + SuiteGenerator::getInstance()->generateAllSuites($testManifest); + $testManifest->generate(); + + print "Generate Tests Command Run" . PHP_EOL; + } + + /** + * Function which builds up a configuration including test and suites for consumption of Magento generation methods. + * + * @param string $json + * @param array $tests + * @param bool $force + * @param bool $verbose + * @return array + */ + private function createTestConfiguration($json, array $tests, bool $force, bool $verbose) + { + // set our application configuration so we can references the user options in our framework + MftfApplicationConfig::create( + $force, + MftfApplicationConfig::GENERATION_PHASE, + $verbose + ); + + $testConfiguration = []; + $testConfiguration['tests'] = $tests; + $testConfiguration['suites'] = []; + + $testConfiguration = $this->parseTestsConfigJson($json, $testConfiguration); + + // if we have references to specific tests, we resolve the test objects and pass them to the config + if (!empty($testConfiguration['tests'])) { + $testObjects = []; + + foreach ($testConfiguration['tests'] as $test) { + $testObjects[$test] = TestObjectHandler::getInstance()->getObject($test); + } + + $testConfiguration['tests'] = $testObjects; + } + + return $testConfiguration; + } + + /** + * Function which takes a json string of potential custom configuration and parses/validates the resulting json + * passed in by the user. The result is a testConfiguration array. + * + * @param string $json + * @param array $testConfiguration + * @throws TestFrameworkException + * @return array + */ + private function parseTestsConfigJson($json, array $testConfiguration) { + if ($json === null) { + return $testConfiguration; + } + + $jsonTestConfiguration = []; + $testConfigArray = json_decode($json, true); + + $jsonTestConfiguration['tests'] = $testConfigArray['tests'] ?? null;; + $jsonTestConfiguration['suites'] = $testConfigArray['suites'] ?? null; + return $jsonTestConfiguration; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php new file mode 100644 index 000000000..6041d0be5 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php @@ -0,0 +1,73 @@ +setName("run:test") + ->setDescription("generation and execution of test(s) defined in xml") + ->addArgument('name', InputArgument::REQUIRED | InputArgument::IS_ARRAY, "name of tests to generate and execute") + ->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "skip generation and execute existing test"); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $tests = $input->getArgument('name'); + $skipGeneration = $input->getOption('skip-generate') ?? false; + + if (!$skipGeneration) { + $command = $this->getApplication()->find('generate:tests'); + $args = [ + '--tests' => json_encode([ + 'tests' => $tests, + 'suites' => null + ]) + ]; + + $command->run(new ArrayInput($args), $output); + } + + // we only generate relevant tests here so we can execute "all tests" + $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . " run functional --verbose --steps"; + + $process = new Process($codeceptionCommand); + $process->setWorkingDirectory(TESTS_BP); + $process->run( + function ($type, $buffer) use ($output) { + $output->write($buffer); + } + ); + } + +} diff --git a/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php new file mode 100644 index 000000000..c0e5d99b7 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Console/RunTestGroupCommand.php @@ -0,0 +1,104 @@ +setName('run:group') + ->setDescription('Execute a set of tests referenced via group annotations') + ->addOption('skip-generate', 'k', InputOption::VALUE_NONE, "only execute a group of tests without generating from source xml") + ->addArgument( + 'groups', + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + 'group names to be executed via codeception' + ); + } + + /** + * Executes the current command. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + * @throws \Symfony\Component\Console\Exception\LogicException + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $skipGeneration = $input->getOption('skip-generate') ?? false; + $groups = $input->getArgument('groups'); + + if (!$skipGeneration) { + $testConfiguration = $this->getGroupAndSuiteConfiguration($groups); + $command = $this->getApplication()->find('generate:tests'); + $args = ['--tests' => $testConfiguration]; + + $command->run(new ArrayInput($args), $output); + } + + $codeceptionCommand = realpath(PROJECT_ROOT . '/vendor/bin/codecept') . ' run functional --verbose --steps'; + + foreach ($groups as $group) { + $codeceptionCommand .= " -g {$group}"; + } + + $process = new Process($codeceptionCommand); + $process->setWorkingDirectory(TESTS_BP); + $process->run( + function ($type, $buffer) use ($output) { + $output->write($buffer); + } + ); + } + + /** + * Returns a json string to be used as an argument for generation of a group or suite + * + * @param array $groups + * @return string + */ + private function getGroupAndSuiteConfiguration(array $groups) + { + $testConfiguration['tests'] = []; + $testConfiguration['suites'] = null; + $availableSuites = SuiteObjectHandler::getInstance()->getAllObjects(); + + foreach ($groups as $group) { + if (array_key_exists($group, $availableSuites)) { + $testConfiguration['suites'][$group] = []; + } + + $testConfiguration['tests'] = array_merge( + $testConfiguration['tests'], + array_keys(TestObjectHandler::getInstance()->getTestsByGroup($group)) + ); + } + + $testConfigurationJson = json_encode($testConfiguration); + return $testConfigurationJson; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php b/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php index e0ef6053a..5b57c43d7 100644 --- a/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php +++ b/src/Magento/FunctionalTestingFramework/Console/SetupEnvCommand.php @@ -31,9 +31,9 @@ class SetupEnvCommand extends Command */ protected function configure() { - $this->setName('setup:env'); - $this->setDescription("Generate .env file."); - $this->envProcessor = new EnvProcessor(BP . DIRECTORY_SEPARATOR . '.env'); + $this->setName('setup:env') + ->setDescription("Generate .env file."); + $this->envProcessor = new EnvProcessor(TESTS_BP . DIRECTORY_SEPARATOR . '.env'); $env = $this->envProcessor->getEnv(); foreach ($env as $key => $value) { $this->addOption($key, null, InputOption::VALUE_REQUIRED, '', $value); @@ -59,6 +59,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $userEnv[$key] = $input->getOption($key); } $this->envProcessor->putEnvFile($userEnv); - $output->writeln(".env configuration successfully applied.\n"); + $output->writeln(".env configuration successfully applied."); } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index 7ae362591..c7b31610a 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -241,7 +241,8 @@ private function generateGroupFile($suiteName, $tests, $originalSuiteName) */ private function appendEntriesToConfig($suiteName, $suitePath, $groupNamespace) { - $relativeSuitePath = substr($suitePath, strlen(dirname(dirname(TESTS_BP))) + 1); + $relativeSuitePath = substr($suitePath, strlen(TESTS_BP)); + $relativeSuitePath = ltrim($relativeSuitePath, DIRECTORY_SEPARATOR); $ymlArray = self::getYamlFileContents(); if (!array_key_exists(self::YAML_GROUPS_TAG, $ymlArray)) { @@ -344,6 +345,6 @@ private static function getYamlFileContents() */ private static function getYamlConfigFilePath() { - return dirname(dirname(TESTS_BP)) . DIRECTORY_SEPARATOR; + return TESTS_BP . DIRECTORY_SEPARATOR; } } diff --git a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php index 7129ff091..53871a098 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php +++ b/src/Magento/FunctionalTestingFramework/Util/Env/EnvProcessor.php @@ -36,6 +36,13 @@ class EnvProcessor */ private $env = []; + /** + * Boolean indicating existence of env file + * + * @var bool + */ + private $envExists; + /** * EnvProcessor constructor. * @param string $envFile @@ -44,33 +51,50 @@ public function __construct( string $envFile = '' ) { $this->envFile = $envFile; - $this->envExampleFile = $envFile . '.example'; + $this->envExists = file_exists($envFile); + $this->envExampleFile = realpath(FW_BP . "/etc/config/.env.example"); } /** - * Serves for parsing '.env.example' file into associative array. + * Serves for parsing '.env' file into associative array. * * @return array */ - public function parseEnvFile(): array + private function parseEnvFile(): array { - $envLines = file( + $envExampleFile = file( $this->envExampleFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ); - $env = []; - foreach ($envLines as $line) { + + $envContents = []; + if ($this->envExists) { + $envFile = file( + $this->envFile, + FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES + ); + + $envContents = $this->parseEnvFileLines($envFile); + } + + return array_diff_key($this->parseEnvFileLines($envExampleFile), $envContents); + } + + private function parseEnvFileLines(array $file): array + { + $fileArray = []; + foreach ($file as $line) { // do not use commented out lines if (strpos($line, '#') !== 0) { list($key, $value) = explode('=', $line); - $env[$key] = $value; + $fileArray[$key] = $value; } } - return $env; + return $fileArray; } /** - * Serves for putting array with environment variables into .env file. + * Serves for putting array with environment variables into .env file or appending new variables we introduce * * @param array $config * @return void @@ -81,7 +105,12 @@ public function putEnvFile(array $config = []) foreach ($config as $key => $value) { $envData .= $key . '=' . $value . PHP_EOL; } - file_put_contents($this->envFile, $envData); + + if ($this->envExists) { + file_put_contents($this->envFile, $envData, FILE_APPEND); + } else { + file_put_contents($this->envFile, $envData); + } } /** diff --git a/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php b/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php index 3c3eb6de9..e5ce1ee50 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/Manifest/BaseTestManifest.php @@ -43,7 +43,8 @@ abstract class BaseTestManifest public function __construct($path, $runConfig, $suiteConfiguration) { $this->runTypeConfig = $runConfig; - $this->relativeDirPath = substr($path, strlen(dirname(dirname(TESTS_BP))) + 1); + $relativeDirPath = substr($path, strlen(TESTS_BP)); + $this->relativeDirPath = ltrim($relativeDirPath, DIRECTORY_SEPARATOR); $this->suiteConfiguration = $suiteConfiguration; } diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php index 43b472b4d..275f8fc57 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver.php @@ -229,6 +229,7 @@ private function aggregateTestModulePaths() // Define the Module paths from default TESTS_MODULE_PATH $modulePath = defined('TESTS_MODULE_PATH') ? TESTS_MODULE_PATH : TESTS_BP; + $modulePath = rtrim($modulePath, DIRECTORY_SEPARATOR); // Define the Module paths from vendor modules $vendorCodePath = PROJECT_ROOT diff --git a/src/Magento/FunctionalTestingFramework/_bootstrap.php b/src/Magento/FunctionalTestingFramework/_bootstrap.php new file mode 100644 index 000000000..3dd871adc --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/_bootstrap.php @@ -0,0 +1,63 @@ +load(); + + if (array_key_exists('TESTS_MODULE_PATH', $_ENV) xor array_key_exists('TESTS_BP', $_ENV)) { + throw new Exception( + 'You must define both parameters TESTS_BP and TESTS_MODULE_PATH or neither parameter' + ); + } + + foreach ($_ENV as $key => $var) { + defined($key) || define($key, $var); + } + + defined('MAGENTO_CLI_COMMAND_PATH') || define( + 'MAGENTO_CLI_COMMAND_PATH', + 'dev/tests/acceptance/utils/command.php' + ); + $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PATH', MAGENTO_CLI_COMMAND_PATH); + + defined('MAGENTO_CLI_COMMAND_PARAMETER') || define('MAGENTO_CLI_COMMAND_PARAMETER', 'command'); + $env->setEnvironmentVariable('MAGENTO_CLI_COMMAND_PARAMETER', MAGENTO_CLI_COMMAND_PARAMETER); +} + +// TODO REMOVE THIS CODE ONCE WE HAVE STOPPED SUPPORTING dev/tests/acceptance PATH +// define TEST_PATH and TEST_MODULE_PATH +defined('TESTS_BP') || define('TESTS_BP', realpath(MAGENTO_BP . DIRECTORY_SEPARATOR . 'dev/tests/acceptance/')); + +$RELATIVE_TESTS_MODULE_PATH = '/tests/functional/Magento/FunctionalTest'; +defined('TESTS_MODULE_PATH') || define( + 'TESTS_MODULE_PATH', + realpath(TESTS_BP . $RELATIVE_TESTS_MODULE_PATH) +); + +// add the debug flag here +$debugMode = $_ENV['MFTF_DEBUG'] ?? false; +if (!(bool)$debugMode && extension_loaded('xdebug')) { + xdebug_disable(); +}