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