12 use Symfony\Component\HttpFoundation\Response;
    34     const PATHS = [
'module', 
'library', 
'theme', 
'language'];
   100     private static $instance = 
null;
   115         'SampleTests', 
'SampleTemplates'   125         if (!self::$instance) {
   128         return self::$instance;
   134     private function __construct()
   149         if (isset($this->enabledModules)) {
   154             $this->printMagentoVersionInfo();
   162             'Authorization: Bearer ' . 
$token,
   164         $ch = curl_init(
$url);
   165         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 
"GET");
   166         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 
true);
   167         curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
   168         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 
false);
   169         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 
false);
   173             $message = 
"Could not retrieve Modules from Magento Instance.";
   175                 "Admin Module List Url" => 
$url,
   176                 "MAGENTO_ADMIN_USERNAME" => getenv(
"MAGENTO_ADMIN_USERNAME"),
   177                 "MAGENTO_ADMIN_PASSWORD" => getenv(
"MAGENTO_ADMIN_PASSWORD"),
   182         $this->enabledModules = json_decode(
$response);
   194         $moduleWhitelist = getenv(self::MODULE_WHITELIST);
   196         if (empty($moduleWhitelist)) {
   199         return array_map(
'trim', explode(
',', $moduleWhitelist));
   209         if (isset($this->enabledModulePaths)) {
   213         $allModulePaths = $this->aggregateTestModulePaths();
   221         $enabledDirectoryPaths = $this->getEnabledDirectoryPaths(
$enabledModules, $allModulePaths);
   232     private function aggregateTestModulePaths()
   234         $allModulePaths = [];
   237         $magentoBaseCodePath = MAGENTO_BP;
   240         $modulePath = defined(
'TESTS_MODULE_PATH') ? TESTS_MODULE_PATH : TESTS_BP;
   241         $modulePath = rtrim($modulePath, DIRECTORY_SEPARATOR);
   243         $vendorCodePath = DIRECTORY_SEPARATOR . 
"vendor";
   244         $appCodePath = DIRECTORY_SEPARATOR . 
"app" . DIRECTORY_SEPARATOR . 
"code";
   246         $codePathsToPattern = [
   248             $magentoBaseCodePath . $vendorCodePath => 
'Test' . DIRECTORY_SEPARATOR . 
'Mftf',
   249             $magentoBaseCodePath . $appCodePath => 
'Test' . DIRECTORY_SEPARATOR . 
'Mftf'   252         foreach ($codePathsToPattern as $codePath => 
$pattern) {
   253             $allModulePaths = array_merge_recursive($allModulePaths, $this->globRelevantPaths($codePath, 
$pattern));
   256         return $allModulePaths;
   268     private function globRelevantPaths($testPath, 
$pattern)
   273         if (file_exists($testPath)) {
   274             $relevantPaths = $this->globRelevantWrapper($testPath, 
$pattern);
   277         $allComponents = $this->getRegisteredModuleList();
   279         foreach ($relevantPaths as $codePath) {
   280             $mainModName = array_search($codePath, $allComponents) ?: basename(str_replace(
$pattern, 
'', $codePath));
   281             $mainModName = str_replace(self::MAGENTO_PREFIX, 
"", $mainModName);
   282             $modulePaths[$mainModName][] = $codePath;
   287                     [
'module' => $mainModName, 
'path' => $codePath]
   302     private static function globRelevantWrapper($testPath, 
$pattern)
   305             return glob($testPath . 
'*' . DIRECTORY_SEPARATOR . 
'*' . 
$pattern);
   307         $subDirectory = 
"*" . DIRECTORY_SEPARATOR;
   308         $directories = glob($testPath . $subDirectory . 
$pattern, GLOB_ONLYDIR);
   309         foreach (glob($testPath . $subDirectory, GLOB_ONLYDIR) as $dir) {
   310             $directories = array_merge_recursive($directories, self::globRelevantWrapper($dir, 
$pattern));
   321     private function flattenAllModulePaths($modulePaths)
   323         $it = new \RecursiveIteratorIterator(
new \RecursiveArrayIterator($modulePaths));
   339     private function getEnabledDirectoryPaths(
$enabledModules, $allModulePaths)
   341         $enabledDirectoryPaths = [];
   344             $moduleShortName = explode(
'_', $magentoModuleName)[1] ?? $magentoModuleName;
   345             if (!isset($this->knownDirectories[$moduleShortName]) && !isset($allModulePaths[$moduleShortName])) {
   347             } 
elseif (isset($this->knownDirectories[$moduleShortName]) && !isset($allModulePaths[$moduleShortName])) {
   349                     "Known directory could not match to an existing path.",
   350                     [
'knownDirectory' => $moduleShortName]
   353                 $enabledDirectoryPaths[$moduleShortName] = $allModulePaths[$moduleShortName];
   356         return $enabledDirectoryPaths;
   364     private function printMagentoVersionInfo()
   371             "Fetching version information.",
   375         $ch = curl_init(
$url);
   376         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 
"GET");
   377         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 
true);
   381             $response = 
"No version information available.";
   385             'version information',
   397         $login = $_ENV[
'MAGENTO_ADMIN_USERNAME'] ?? 
null;
   398         $password = $_ENV[
'MAGENTO_ADMIN_PASSWORD'] ?? 
null;
   399         if (!$login || !$password || !isset($_ENV[
'MAGENTO_BASE_URL'])) {
   400             $message = 
"Cannot retrieve API token without credentials and base url, please fill out .env.";
   402                 "MAGENTO_BASE_URL" => getenv(
"MAGENTO_BASE_URL"),
   403                 "MAGENTO_ADMIN_USERNAME" => getenv(
"MAGENTO_ADMIN_USERNAME"),
   404                 "MAGENTO_ADMIN_PASSWORD" => getenv(
"MAGENTO_ADMIN_PASSWORD"),
   411             'username' => $login,
   412             'password' => $password
   415             'Content-Type: application/json',
   418         $ch = curl_init(
$url);
   419         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 
"POST");
   420         curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(
$data));
   421         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 
true);
   422         curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
   423         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 
false);
   424         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 
false);
   427         $responseCode = curl_getinfo($ch)[
'http_code'];
   429         if ($responseCode !== 200) {
   430             if ($responseCode == 0) {
   431                 $details = 
"Could not find Magento Instance at given MAGENTO_BASE_URL";
   433                 $details = $responseCode . 
" " . Response::$statusTexts[$responseCode];
   436             $message = 
"Could not retrieve API token from Magento Instance ({$details})";
   439                 "responseCode" => $responseCode,
   440                 "MAGENTO_ADMIN_USERNAME" => getenv(
"MAGENTO_ADMIN_USERNAME"),
   441                 "MAGENTO_ADMIN_PASSWORD" => getenv(
"MAGENTO_ADMIN_PASSWORD"),
   457         return $this->sequenceSorter->sort(
$files);
   468         $modulePathsResult = $this->removeBlacklistModules($modulesPath);
   469         $customModulePaths = $this->getCustomModulePaths();
   471         array_map(
function (
$value) {
   473                 "including custom module",
   476         }, $customModulePaths);
   478         return $this->flattenAllModulePaths(array_merge($modulePathsResult, $customModulePaths));
   487     private function removeBlacklistModules($modulePaths)
   489         $modulePathsResult = $modulePaths;
   490         foreach ($modulePathsResult as $moduleName => $modulePath) {
   491             if (in_array($moduleName, $this->getModuleBlacklist())) {
   492                 unset($modulePathsResult[$moduleName]);
   495                     [
'module' => $moduleName]
   500         return $modulePathsResult;
   508     private function getCustomModulePaths()
   510         $customModulePaths = getenv(self::CUSTOM_MODULE_PATHS);
   512         if (!$customModulePaths) {
   516         return array_map(
'trim', explode(
',', $customModulePaths));
   524     private function getModuleBlacklist()
   534     private function getRegisteredModuleList()
   536         if (array_key_exists(
'MAGENTO_BP', $_ENV)) {
   537             $autoloadPath = realpath(MAGENTO_BP . 
"/app/autoload.php");
   539                 require_once($autoloadPath);
   541                 throw new TestFrameworkException(
"Magento app/autoload.php not found with given MAGENTO_BP:"   549                 throw new TestFrameworkException(
"Magento Installation not found when loading registered modules.\n");
   551             $components = new \Magento\Framework\Component\ComponentRegistrar();
   552             foreach (self::PATHS as $componentType) {
   553                 $allComponents = array_merge($allComponents, $components->getPaths($componentType));
   555             array_walk($allComponents, 
function (&
$value) {
   556                 $value .= DIRECTORY_SEPARATOR . 
'Test' . DIRECTORY_SEPARATOR . 
'Mftf';
   558             return $allComponents;
   559         } 
catch (TestFrameworkException $e) {
 
elseif(isset( $params[ 'redirect_parent']))
 
static getObjectManager()
 
const CUSTOM_MODULE_PATHS
 
applyCustomModuleMethods($modulesPath)
 
sortFilesByModuleSequence(array $files)
 
foreach($appDirs as $dir) $files