47 private $exportDirectory;
54 private $exportDirName;
68 private $consoleOutput;
82 private $currentGenerationScope;
91 private function __construct($exportDir, $tests, $debug =
false)
96 $this->exportDirectory = TESTS_MODULE_PATH
101 $this->
tests = $tests;
102 $this->consoleOutput = new \Symfony\Component\Console\Output\ConsoleOutput();
103 $this->debug = $debug;
114 public static function getInstance($dir =
null, $tests = [], $debug =
false)
126 return $this->exportDirectory;
136 private function loadAllTestObjects($testsToIgnore)
138 if ($this->
tests ===
null || empty($this->
tests)) {
140 return array_diff_key($testObjects, $testsToIgnore);
145 $invalidTestObjects = array_intersect_key($this->
tests, $testsToIgnore);
146 if (!empty($invalidTestObjects)) {
147 throw new TestReferenceException(
148 "Cannot reference test configuration for generation without accompanying suite.",
149 [
'tests' => array_keys($invalidTestObjects)]
165 private function createCestFile($testPhp, $filename)
167 $exportFilePath = $this->exportDirectory . DIRECTORY_SEPARATOR . $filename .
".php";
168 $file =
fopen($exportFilePath,
'w');
171 throw new \Exception(
"Could not open the file.");
190 if ($this->
tests ===
null) {
196 if ($testsToIgnore ===
null) {
200 $testPhpArray = $this->assembleAllTestPhp($testManifest, $testsToIgnore);
201 foreach ($testPhpArray as $testPhpFile) {
202 $this->createCestFile($testPhpFile[1], $testPhpFile[0]);
215 private function assembleTestPhp($testObject)
217 $usePhp = $this->generateUseStatementsPhp();
218 $classAnnotationsPhp = $this->generateAnnotationsPhp($testObject->getAnnotations());
220 $className = $testObject->getCodeceptionName();
222 if (!$testObject->isSkipped()) {
223 $hookPhp = $this->generateHooksPhp($testObject->getHooks());
227 $testsPhp = $this->generateTestPhp($testObject);
228 }
catch (TestReferenceException $e) {
229 throw new TestReferenceException($e->getMessage() .
"\n" . $testObject->getFilename());
232 $cestPhp =
"<?php\n";
233 $cestPhp .=
"namespace Magento\AcceptanceTest\\_" . $this->exportDirName .
"\Backend;\n\n";
235 $cestPhp .= $classAnnotationsPhp;
236 $cestPhp .= sprintf(
"class %s\n",
$className);
238 $cestPhp .= $hookPhp;
239 $cestPhp .= $testsPhp;
252 private function assembleAllTestPhp($testManifest, array $testsToIgnore)
255 $testObjects = $this->loadAllTestObjects($testsToIgnore);
258 foreach ($testObjects as $test) {
260 if ($test->isSkipped() && !empty($test->getParentName())) {
263 }
catch (TestReferenceException $e) {
264 print(
"{$test->getName()} will not be generated. Parent {$e->getMessage()} \n");
269 $this->debug(
"<comment>Start creating test: " . $test->getCodeceptionName() .
"</comment>");
270 $php = $this->assembleTestPhp($test);
271 $cestPhpArray[] = [$test->getCodeceptionName(), $php];
273 $debugInformation = $test->getDebugInformation();
274 $this->debug($debugInformation);
275 $this->debug(
"<comment>Finish creating test: " . $test->getCodeceptionName() .
"</comment>" . PHP_EOL);
278 if ($testManifest !=
null) {
279 $testManifest->addTest($test);
283 return $cestPhpArray;
292 private function debug($messages)
294 if ($this->debug && $messages) {
295 $messages = (array)$messages;
297 $this->consoleOutput->writeln(
$message);
308 private function generateUseStatementsPhp()
310 $useStatementsPhp =
"use Magento\FunctionalTestingFramework\AcceptanceTester;\n";
311 $useStatementsPhp .=
"use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;\n";
312 $useStatementsPhp .=
"use Magento\FunctionalTestingFramework\DataGenerator\Handlers\PersistedObjectHandler;\n";
313 $useStatementsPhp .=
"use \Codeception\Util\Locator;\n";
315 $allureStatements = [
316 "Yandex\Allure\Adapter\Annotation\Features;",
317 "Yandex\Allure\Adapter\Annotation\Stories;",
318 "Yandex\Allure\Adapter\Annotation\Title;",
319 "Yandex\Allure\Adapter\Annotation\Description;",
320 "Yandex\Allure\Adapter\Annotation\Parameter;",
321 "Yandex\Allure\Adapter\Annotation\Severity;",
322 "Yandex\Allure\Adapter\Model\SeverityLevel;",
323 "Yandex\Allure\Adapter\Annotation\TestCaseId;\n" 326 foreach ($allureStatements as $allureUseStatement) {
327 $useStatementsPhp .= sprintf(
"use %s\n", $allureUseStatement);
330 return $useStatementsPhp;
340 private function generateAnnotationsPhp($annotationsObject, $isMethod =
false)
349 $annotationsPhp =
"{$indent}/**\n";
351 foreach ($annotationsObject as $annotationType => $annotationName) {
353 if ($annotationType ==
"useCaseId") {
357 $annotationsPhp .= $this->generateClassAnnotations($annotationType, $annotationName);
359 $annotationsPhp .= $this->generateMethodAnnotations($annotationType, $annotationName);
364 $annotationsPhp .= $this->generateMethodAnnotations();
367 $annotationsPhp .=
"{$indent} */\n";
369 return $annotationsPhp;
379 private function generateMethodAnnotations($annotationType =
null, $annotationName =
null)
384 switch ($annotationType) {
387 foreach ($annotationName as
$name) {
388 $features .= sprintf(
"\"%s\"",
$name);
390 if (next($annotationName)) {
399 foreach ($annotationName as
$name) {
400 $stories .= sprintf(
"\"%s\"",
$name);
402 if (next($annotationName)) {
410 $annotationToAppend = sprintf(
"{$indent} * @Severity(level = SeverityLevel::%s)\n", $annotationName[0]);
415 "{$indent} * @Parameter(name = \"%s\", value=\"$%s\")\n",
436 private function generateClassAnnotations($annotationType, $annotationName)
440 switch ($annotationType) {
458 foreach ($annotationName as
$group) {
485 $this->currentGenerationScope = $generationScope;
487 foreach ($actionObjects as $actionObject) {
488 $stepKey = $actionObject->getStepKey();
489 $customActionAttributes = $actionObject->getCustomActionAttributes();
495 $parameterArray =
null;
496 $returnVariable =
null;
508 $requiredAction =
null;
512 $dependentSelector =
null;
520 $assertExpected =
null;
521 $assertActual =
null;
522 $assertMessage =
null;
523 $assertIsStrict =
null;
527 $this->validateXmlAttributesMutuallyExclusive($stepKey, $actionObject->getType(), $customActionAttributes);
529 if (isset($customActionAttributes[
'command'])) {
530 $command = $this->addUniquenessFunctionCall($customActionAttributes[
'command']);
532 if (isset($customActionAttributes[
'arguments'])) {
533 $arguments = $this->addUniquenessFunctionCall($customActionAttributes[
'arguments']);
536 if (isset($customActionAttributes[
'attribute'])) {
537 $attribute = $customActionAttributes[
'attribute'];
540 if (isset($customActionAttributes[
'sortOrder'])) {
541 $sortOrder = $customActionAttributes[
'sortOrder'];
544 if (isset($customActionAttributes[
'userInput']) && isset($customActionAttributes[
'url'])) {
545 $input = $this->addUniquenessFunctionCall($customActionAttributes[
'userInput']);
546 $url = $this->addUniquenessFunctionCall($customActionAttributes[
'url']);
547 }
elseif (isset($customActionAttributes[
'userInput'])) {
548 $input = $this->addUniquenessFunctionCall($customActionAttributes[
'userInput']);
549 }
elseif (isset($customActionAttributes[
'url'])) {
550 $input = $this->addUniquenessFunctionCall($customActionAttributes[
'url']);
551 $url = $this->addUniquenessFunctionCall($customActionAttributes[
'url']);
552 }
elseif (isset($customActionAttributes[
'expectedValue'])) {
554 $assertExpected = $this->addUniquenessFunctionCall($customActionAttributes[
'expectedValue']);
555 }
elseif (isset($customActionAttributes[
'regex'])) {
556 $input = $this->addUniquenessFunctionCall($customActionAttributes[
'regex']);
559 if (isset($customActionAttributes[
'date']) && isset($customActionAttributes[
'format'])) {
560 $input = $this->addUniquenessFunctionCall($customActionAttributes[
'date']);
564 $format = $this->addUniquenessFunctionCall($customActionAttributes[
'format']);
570 if (isset($customActionAttributes[
'expected'])) {
571 $assertExpected = $this->resolveValueByType(
572 $customActionAttributes[
'expected'],
573 isset($customActionAttributes[
'expectedType']) ? $customActionAttributes[
'expectedType'] :
null 576 if (isset($customActionAttributes[
'actual'])) {
577 $assertActual = $this->resolveValueByType(
578 $customActionAttributes[
'actual'],
579 isset($customActionAttributes[
'actualType']) ? $customActionAttributes[
'actualType'] :
null 582 if (isset($customActionAttributes[
'message'])) {
583 $assertMessage = $this->addUniquenessFunctionCall($customActionAttributes[
'message']);
585 if (isset($customActionAttributes[
'delta'])) {
586 $assertDelta = $this->resolveValueByType($customActionAttributes[
'delta'],
"float");
588 if (isset($customActionAttributes[
'strict'])) {
589 $assertIsStrict = $this->resolveValueByType($customActionAttributes[
'strict'],
"bool");
592 if (isset($customActionAttributes[
'time'])) {
593 $time = $customActionAttributes[
'time'];
595 if (isset($customActionAttributes[
'timeout'])) {
596 $time = $customActionAttributes[
'timeout'];
599 if (isset($customActionAttributes[
'parameterArray']) && $actionObject->getType() !=
'pressKey') {
601 $this->validateParameterArray($customActionAttributes[
'parameterArray']);
603 $parameterArray =
"[";
604 $parameterArray .= $this->addUniquenessToParamArray($customActionAttributes[
'parameterArray']);
605 $parameterArray .=
"]";
608 if (isset($customActionAttributes[
'requiredAction'])) {
609 $requiredAction = $customActionAttributes[
'requiredAction'];
612 if (isset($customActionAttributes[
'selectorArray'])) {
613 $selector = $customActionAttributes[
'selectorArray'];
614 }
elseif (isset($customActionAttributes[
'selector'])) {
615 $selector = $this->addUniquenessFunctionCall($customActionAttributes[
'selector']);
616 $selector = $this->resolveLocatorFunctionInAttribute($selector);
619 if (isset($customActionAttributes[
'selector1']) || isset($customActionAttributes[
'filterSelector'])) {
620 $selectorOneValue = $customActionAttributes[
'selector1'] ?? $customActionAttributes[
'filterSelector'];
621 $selector1 = $this->addUniquenessFunctionCall($selectorOneValue);
622 $selector1 = $this->resolveLocatorFunctionInAttribute($selector1);
625 if (isset($customActionAttributes[
'selector2']) || isset($customActionAttributes[
'optionSelector'])) {
626 $selectorTwoValue = $customActionAttributes[
'selector2'] ?? $customActionAttributes[
'optionSelector'];
627 $selector2 = $this->addUniquenessFunctionCall($selectorTwoValue);
628 $selector2 = $this->resolveLocatorFunctionInAttribute($selector2);
631 if (isset($customActionAttributes[
'x'])) {
632 $x = $customActionAttributes[
'x'];
635 if (isset($customActionAttributes[
'y'])) {
636 $y = $customActionAttributes[
'y'];
639 if (isset($customActionAttributes[
'function'])) {
640 $function = $this->addUniquenessFunctionCall($customActionAttributes[
'function']);
643 $function = trim($function,
'"');
646 if ($actionObject->getType() ==
"executeJS") {
647 $function = preg_replace(
'/(?<!{)(\$[A-Za-z._]+)(?![A-z.]*+\$)/',
'\\\\$1', $function);
651 if (isset($customActionAttributes[
'html'])) {
652 $html = $customActionAttributes[
'html'];
655 if (isset($customActionAttributes[
'locale'])) {
656 $locale = $this->wrapWithDoubleQuotes($customActionAttributes[
'locale']);
659 if (isset($customActionAttributes[
'username'])) {
660 $username = $this->wrapWithDoubleQuotes($customActionAttributes[
'username']);
663 if (isset($customActionAttributes[
'password'])) {
664 $password = $this->wrapWithDoubleQuotes($customActionAttributes[
'password']);
667 if (isset($customActionAttributes[
'width'])) {
668 $width = $customActionAttributes[
'width'];
671 if (isset($customActionAttributes[
'height'])) {
672 $height = $customActionAttributes[
'height'];
675 if (isset($customActionAttributes[
'value'])) {
676 $value = $this->wrapWithDoubleQuotes($customActionAttributes[
'value']);
679 if (isset($customActionAttributes[
'button'])) {
680 $button = $this->wrapWithDoubleQuotes($customActionAttributes[
'button']);
683 if (isset($customActionAttributes[
'parameter'])) {
684 $parameter = $this->wrapWithDoubleQuotes($customActionAttributes[
'parameter']);
687 if (isset($customActionAttributes[
'dependentSelector'])) {
688 $dependentSelector = $this->addUniquenessFunctionCall($customActionAttributes[
'dependentSelector']);
691 if (isset($customActionAttributes[
'visible'])) {
692 $visible = $customActionAttributes[
'visible'];
695 if (isset($customActionAttributes[
'storeCode'])) {
696 $storeCode = $customActionAttributes[
'storeCode'];
698 switch ($actionObject->getType()) {
700 $entity = $customActionAttributes[
'entity'];
702 $testSteps .= sprintf(
703 "\t\t$%s->amGoingTo(\"create entity that has the stepKey: %s\");\n",
709 $customEntityFields =
712 $requiredEntityKeys = [];
713 foreach ($actionObject->getCustomActionAttributes() as $actionAttribute) {
714 if (is_array($actionAttribute) && $actionAttribute[
'nodeName'] ==
'requiredEntity') {
716 $requiredEntityActionGroup = $actionAttribute[
'actionGroup'] ??
null;
717 $requiredEntityKeys[] = $actionAttribute[
'createDataKey'] . $requiredEntityActionGroup;
721 $requiredEntityKeysArray =
"";
722 if (!empty($requiredEntityKeys)) {
723 $requiredEntityKeysArray =
'"' . implode(
'", "', $requiredEntityKeys) .
'"';
733 $createEntityFunctionCall =
"\t\tPersistedObjectHandler::getInstance()->createEntity(";
734 $createEntityFunctionCall .=
"\n\t\t\t\"{$stepKey}\",";
735 $createEntityFunctionCall .=
"\n\t\t\t\"{$scope}\",";
736 $createEntityFunctionCall .=
"\n\t\t\t\"{$entity}\"";
737 $createEntityFunctionCall .=
",\n\t\t\t[{$requiredEntityKeysArray}]";
738 if (count($customEntityFields) > 1) {
739 $createEntityFunctionCall .=
",\n\t\t\t\${$stepKey}Fields";
741 $createEntityFunctionCall .=
",\n\t\t\tnull";
744 $createEntityFunctionCall .=
",\n\t\t\t\"{$storeCode}\"";
746 $createEntityFunctionCall .=
"\n\t\t);\n";
747 $testSteps .= $createEntityFunctionCall;
750 if (isset($customActionAttributes[
'createDataKey'])) {
751 $key = $this->resolveStepKeyReferences(
752 $customActionAttributes[
'createDataKey'],
753 $actionObject->getActionOrigin(),
756 $actionGroup = $actionObject->getCustomActionAttributes()[
'actionGroup'] ??
null;
757 $key .= $actionGroup;
759 $contextSetter = sprintf(
760 "\t\t$%s->amGoingTo(\"delete entity that has the createDataKey: %s\");\n",
773 $deleteEntityFunctionCall =
"\t\tPersistedObjectHandler::getInstance()->deleteEntity(";
774 $deleteEntityFunctionCall .=
"\n\t\t\t\"{$key}\",";
775 $deleteEntityFunctionCall .=
"\n\t\t\t\"{$scope}\"";
776 $deleteEntityFunctionCall .=
"\n\t\t);\n";
778 $testSteps .= $contextSetter;
779 $testSteps .= $deleteEntityFunctionCall;
781 $url = $this->resolveAllRuntimeReferences([
$url])[0];
782 $url = $this->resolveTestVariable([
$url],
null)[0];
784 "\t\t$%s->deleteEntityByUrl(%s);\n",
792 $key = $this->resolveStepKeyReferences(
793 $customActionAttributes[
'createDataKey'],
794 $actionObject->getActionOrigin(),
797 $updateEntity = $customActionAttributes[
'entity'];
798 $actionGroup = $actionObject->getCustomActionAttributes()[
'actionGroup'] ??
null;
799 $key .= $actionGroup;
802 $testSteps .= sprintf(
803 "\t\t$%s->amGoingTo(\"update entity that has the createdDataKey: %s\");\n",
809 $requiredEntityKeys = [];
810 foreach ($actionObject->getCustomActionAttributes() as $actionAttribute) {
811 if (is_array($actionAttribute) && $actionAttribute[
'nodeName'] ==
'requiredEntity') {
813 $requiredEntityActionGroup = $actionAttribute[
'actionGroup'] ??
null;
814 $requiredEntityKeys[] = $actionAttribute[
'createDataKey'] . $requiredEntityActionGroup;
817 $requiredEntityKeysArray =
"";
818 if (!empty($requiredEntityKeys)) {
819 $requiredEntityKeysArray =
'"' . implode(
'", "', $requiredEntityKeys) .
'"';
829 $updateEntityFunctionCall =
"\t\tPersistedObjectHandler::getInstance()->updateEntity(";
830 $updateEntityFunctionCall .=
"\n\t\t\t\"{$key}\",";
831 $updateEntityFunctionCall .=
"\n\t\t\t\"{$scope}\",";
832 $updateEntityFunctionCall .=
"\n\t\t\t\"{$updateEntity}\"";
833 $updateEntityFunctionCall .=
",\n\t\t\t[{$requiredEntityKeysArray}]";
835 $updateEntityFunctionCall .=
",\n\t\t\t\"{$storeCode}\"";
837 $updateEntityFunctionCall .=
"\n\t\t);\n";
838 $testSteps .= $updateEntityFunctionCall;
842 $entity = $customActionAttributes[
'entity'];
844 if (isset($customActionAttributes[
'index'])) {
845 $index = (int)$customActionAttributes[
'index'];
848 $testSteps .= sprintf(
849 "\t\t$%s->amGoingTo(\"get entity that has the stepKey: %s\");\n",
855 $requiredEntityKeys = [];
856 foreach ($actionObject->getCustomActionAttributes() as $actionAttribute) {
857 if (is_array($actionAttribute) && $actionAttribute[
'nodeName'] ==
'requiredEntity') {
858 $requiredEntityActionGroup = $actionAttribute[
'actionGroup'] ??
null;
859 $requiredEntityKeys[] = $actionAttribute[
'createDataKey'] . $requiredEntityActionGroup;
862 $requiredEntityKeysArray =
"";
863 if (!empty($requiredEntityKeys)) {
864 $requiredEntityKeysArray =
'"' . implode(
'", "', $requiredEntityKeys) .
'"';
876 $getEntityFunctionCall =
"\t\tPersistedObjectHandler::getInstance()->getEntity(";
877 $getEntityFunctionCall .=
"\n\t\t\t\"{$stepKey}\",";
878 $getEntityFunctionCall .=
"\n\t\t\t\"{$scope}\",";
879 $getEntityFunctionCall .=
"\n\t\t\t\"{$entity}\"";
880 $getEntityFunctionCall .=
",\n\t\t\t[{$requiredEntityKeysArray}]";
882 $getEntityFunctionCall .=
",\n\t\t\t\"{$storeCode}\"";
884 $getEntityFunctionCall .=
",\n\t\t\tnull";
887 $getEntityFunctionCall .=
",\n\t\t\t{$index}";
889 $getEntityFunctionCall .=
"\n\t\t);\n";
890 $testSteps .= $getEntityFunctionCall;
893 case "assertArrayIsSorted":
894 $testSteps .= $this->wrapFunctionCall(
898 $this->wrapWithDoubleQuotes($sortOrder)
901 case "seeCurrentUrlEquals":
902 case "seeCurrentUrlMatches":
903 case "dontSeeCurrentUrlEquals":
904 case "dontSeeCurrentUrlMatches":
906 case "saveSessionSnapshot":
908 case "seeInCurrentUrl":
909 case "switchToIFrame":
910 case "switchToWindow":
914 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $input, $selector);
916 case "switchToNextTab":
917 case "switchToPreviousTab":
918 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $input);
920 case "clickWithLeftButton":
921 case "clickWithRightButton":
922 case "moveMouseOver":
927 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $selector, $x, $y);
929 case "dontSeeCookie":
932 $testSteps .= $this->wrapFunctionCall(
940 $testSteps .= $this->wrapFunctionCallWithReturnValue(
948 case "dontSeeElement":
949 case "dontSeeElementInDOM":
950 case "dontSeeInFormFields":
952 case "seeElementInDOM":
953 case "seeInFormFields":
954 $testSteps .= $this->wrapFunctionCall(
962 $parameterArray = $customActionAttributes[
'parameterArray'] ??
null;
963 if ($parameterArray) {
964 $parameterArray = $this->processPressKey($parameterArray);
966 $testSteps .= $this->wrapFunctionCall(
975 case "unselectOption":
976 $testSteps .= $this->wrapFunctionCall(
985 $testSteps .= $this->wrapFunctionCall(
994 $testSteps .= $this->wrapFunctionCall(
1003 case "selectMultipleOptions":
1004 $testSteps .= $this->wrapFunctionCall(
1013 case "executeInSelenium":
1014 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $function);
1017 $testSteps .= $this->wrapFunctionCallWithReturnValue(
1025 case "waitForElementChange":
1026 $testSteps .= $this->wrapFunctionCall(
1035 $testSteps .= $this->wrapFunctionCall(
1043 case "waitForAjaxLoad":
1044 case "waitForElement":
1045 case "waitForElementVisible":
1046 case "waitForElementNotVisible":
1047 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $selector, $time);
1049 case "waitForPageLoad":
1051 $testSteps .= $this->wrapFunctionCall(
1060 $testSteps .= $this->wrapFunctionCallWithReturnValue(
1069 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $input, $locale);
1071 case "grabAttributeFrom":
1072 case "grabMultiple":
1073 case "grabFromCurrentUrl":
1074 $testSteps .= $this->wrapFunctionCallWithReturnValue(
1082 case "grabTextFrom":
1083 case "grabValueFrom":
1084 $testSteps .= $this->wrapFunctionCallWithReturnValue(
1091 case "grabPageSource":
1092 $testSteps .= $this->wrapFunctionCallWithReturnValue(
1098 case "resizeWindow":
1099 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $width, $height);
1101 case "searchAndMultiSelectOption":
1102 $testSteps .= $this->wrapFunctionCall(
1113 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $input,
$url);
1116 $testSteps .= $this->wrapFunctionCall(
1126 case "amOnSubdomain":
1131 case "dontSeeInField":
1132 case "dontSeeInCurrentUrl":
1133 case "dontSeeInTitle":
1134 case "dontSeeInPageSource":
1135 case "dontSeeOptionIsSelected":
1137 case "loadSessionSnapshot":
1139 case "seeOptionIsSelected":
1140 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $selector, $input);
1142 case "seeNumberOfElements":
1143 $testSteps .= $this->wrapFunctionCall(
1151 case "seeInPageSource":
1153 case "dontSeeInSource":
1155 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $html);
1157 case "conditionalClick":
1158 $testSteps .= $this->wrapFunctionCall(
1166 case "assertEquals":
1167 case "assertGreaterOrEquals":
1168 case "assertGreaterThan":
1169 case "assertGreaterThanOrEqual":
1170 case "assertInternalType":
1171 case "assertLessOrEquals":
1172 case "assertLessThan":
1173 case "assertLessThanOrEqual":
1174 case "assertNotEquals":
1175 case "assertInstanceOf":
1176 case "assertNotInstanceOf":
1177 case "assertNotRegExp":
1178 case "assertNotSame":
1179 case "assertRegExp":
1181 case "assertStringStartsNotWith":
1182 case "assertStringStartsWith":
1183 case "assertArrayHasKey":
1184 case "assertArrayNotHasKey":
1186 case "assertContains":
1187 case "assertNotContains":
1188 case "expectException":
1189 $testSteps .= $this->wrapFunctionCall(
1198 case "assertElementContainsAttribute":
1200 if (empty($assertExpected)) {
1201 $assertExpected =
'""';
1204 $testSteps .= $this->wrapFunctionCall(
1214 case "assertFileExists":
1215 case "assertFileNotExists":
1216 case "assertIsEmpty":
1217 case "assertNotEmpty":
1218 case "assertNotNull":
1221 $testSteps .= $this->wrapFunctionCall(
1228 case "assertArraySubset":
1229 $testSteps .= $this->wrapFunctionCall(
1239 $testSteps .= $this->wrapFunctionCall(
1246 $testSteps .= $this->wrapFunctionCallWithReturnValue(
1253 $testSteps .= sprintf(
1254 "\t\t$%s->comment(\$%s);\n",
1260 $fieldKey = $actionObject->getCustomActionAttributes()[
'key'];
1261 $input = $this->resolveTestVariable(
1263 $actionObject->getActionOrigin()
1266 $argRef .= str_replace(ucfirst($fieldKey),
"", $stepKey) .
"Fields['{$fieldKey}'] = ${input};\n";
1267 $testSteps .= $argRef;
1269 case "generateDate":
1270 $timezone = getenv(
"DEFAULT_TIMEZONE");
1271 if (isset($customActionAttributes[
'timezone'])) {
1272 $timezone = $customActionAttributes[
'timezone'];
1275 $dateGenerateCode =
"\t\t\$date = new \DateTime();\n";
1276 $dateGenerateCode .=
"\t\t\$date->setTimestamp(strtotime({$input}));\n";
1277 $dateGenerateCode .=
"\t\t\$date->setTimezone(new \DateTimeZone(\"{$timezone}\"));\n";
1278 $dateGenerateCode .=
"\t\t\${$stepKey} = \$date->format({$format});\n";
1280 $testSteps .= $dateGenerateCode;
1282 case "skipReadinessCheck":
1283 $testSteps .= $this->wrapFunctionCall($actor, $actionObject, $customActionAttributes[
'state']);
1286 $testSteps .= $this->wrapFunctionCall(
1305 private function resolveLocatorFunctionInAttribute(
$attribute)
1307 if (strpos(
$attribute,
"Locator::") !==
false) {
1323 private function resolveTestVariable($args, $actionOrigin)
1326 foreach ($args as $key => $arg) {
1327 if ($arg ===
null) {
1332 preg_match_all(
'/\${1,2}[\w.\[\]]+\${1,2}/', $outputArg, $matches);
1333 $this->replaceMatchesIntoArg($matches[0], $outputArg);
1336 $outputArg = $this->trimVariableIfNeeded($outputArg);
1338 $outputArg = $this->resolveStepKeyReferences($outputArg, $actionOrigin);
1340 $newArgs[$key] = $outputArg;
1352 private function trimVariableIfNeeded($input)
1354 preg_match(
'/"{\$[a-z][a-zA-Z\d]+}"/', $input, $match);
1355 if (isset($match[0])) {
1356 return trim($input,
'{}"');
1370 private function replaceMatchesIntoArg($matches, &$outputArg)
1373 $matches = array_unique($matches);
1374 foreach ($matches as $match) {
1377 $variable = $this->stripAndSplitReference($match, $delimiter);
1379 throw new \Exception(
1380 "Invalid Persisted Entity Reference: {$match}. 1381 Test persisted entity references must follow {$delimiter}entityStepKey.field{$delimiter} format." 1385 $replacement =
"PersistedObjectHandler::getInstance()->retrieveEntityField";
1386 $replacement .=
"('{$variable[0]}', '$variable[1]', '{$this->currentGenerationScope}')";
1389 if (strpos($outputArg,
"\"") !==
false) {
1390 $outputArg = $this->processQuoteBreaks($match, $outputArg,
$replacement);
1392 $outputArg = str_replace($match,
$replacement, $outputArg);
1406 private function processQuoteBreaks($match, $argument,
$replacement)
1408 $outputArg = str_replace($match,
'" . ' .
$replacement .
' . "', $argument);
1413 $outputArg = preg_replace(
'/(?(?<![\\\\])"" \. )| \. ""/',
"", $outputArg);
1424 private function resolveStepKeyReferences($input, $actionGroupOrigin, $matchAll =
false)
1426 if ($actionGroupOrigin ==
null) {
1434 $stepKeys = $actionGroup->extractStepKeys();
1437 foreach ($stepKeys as $stepKey) {
1439 $stepKeyVarRef =
"$" . $stepKey;
1440 $persistedVarRef =
"PersistedObjectHandler::getInstance()->retrieveEntityField('{$stepKey}'" 1441 .
", 'field', 'test')";
1442 $persistedVarRefInvoked =
"PersistedObjectHandler::getInstance()->retrieveEntityField('" 1443 . $stepKey . $testInvocationKey .
"', 'field', 'test')";
1445 if (strpos(
$output, $stepKeyVarRef) !==
false) {
1446 $output = str_replace($stepKeyVarRef, $stepKeyVarRef . $testInvocationKey,
$output);
1449 if (strpos(
$output, $persistedVarRef) !==
false) {
1450 $output = str_replace($persistedVarRef, $persistedVarRefInvoked,
$output);
1453 if ($matchAll && strpos(
$output, $stepKey) !==
false) {
1454 $output = str_replace($stepKey, $stepKey . $testInvocationKey,
$output);
1467 private function wrapFunctionArgsWithQuotes($functionRegex, $input)
1470 preg_match_all($functionRegex, $input, $matches);
1473 if (!isset($matches[1][0])) {
1477 $allArguments = explode(
',', $matches[1][0]);
1478 foreach ($allArguments as $argument) {
1479 $argument = trim($argument);
1481 if ($argument[0] ==
"[") {
1482 $replacement =
"[" . $this->addUniquenessToParamArray($argument) .
"]";
1483 }
elseif (is_numeric($argument)) {
1486 $replacement = $this->addUniquenessFunctionCall($argument);
1504 private function stripAndSplitReference($reference, $delimiter)
1506 $strippedReference = str_replace($delimiter,
'', $reference);
1507 return explode(
'.', $strippedReference);
1519 private function generateHooksPhp($hookObjects)
1523 foreach ($hookObjects as $hookObject) {
1524 $type = $hookObject->getType();
1525 $dependencies =
'AcceptanceTester $I';
1527 $hooks .=
"\t/**\n";
1528 $hooks .=
"\t * @param AcceptanceTester \$I\n";
1529 $hooks .=
"\t * @throws \Exception\n";
1530 $hooks .=
"\t */\n";
1534 $hookObject->getActions(),
1537 }
catch (TestReferenceException $e) {
1538 throw new TestReferenceException($e->getMessage() .
" in Element \"" .
$type .
"\"");
1541 $hooks .= sprintf(
"\tpublic function _{$type}(%s)\n", $dependencies);
1544 $hooks .=
"\t}\n\n";
1559 private function generateTestPhp($test)
1563 $testName = $test->getName();
1564 $testName = str_replace(
' ',
'', $testName);
1565 $testAnnotations = $this->generateAnnotationsPhp($test->getAnnotations(),
true);
1566 $dependencies =
'AcceptanceTester $I';
1567 if ($test->isSkipped()) {
1568 $skipString =
"This test is skipped due to the following issues:\\n";
1569 $issues = $test->getAnnotations()[
'skip'] ??
null;
1570 if (isset($issues)) {
1571 $skipString .= implode(
"\\n", $issues);
1573 $skipString .=
"No issues have been specified.";
1575 $steps =
"\t\t" .
'$scenario->skip("' . $skipString .
'");' .
"\n";
1576 $dependencies .=
', \Codeception\Scenario $scenario';
1580 }
catch (\Exception $e) {
1581 throw new TestReferenceException($e->getMessage() .
" in Test \"" . $test->getName() .
"\"");
1585 $testPhp .= $testAnnotations;
1586 $testPhp .= sprintf(
"\tpublic function %s(%s)\n", $testName, $dependencies);
1587 $testPhp .=
"\t{\n";
1589 $testPhp .=
"\t}\n";
1600 private function addUniquenessToParamArray($input)
1602 $tempInput = trim($input,
"[]");
1603 $paramArray = explode(
",", $tempInput);
1606 foreach ($paramArray as $param) {
1608 if (preg_match_all(
'/(.+)=>(.+)/', trim($param), $paramMatches)) {
1609 $param1 = $this->addUniquenessToParamArray($paramMatches[1][0]);
1610 $param2 = $this->addUniquenessToParamArray($paramMatches[2][0]);
1611 $result[] = trim($param1) .
" => " . trim($param2);
1616 if (preg_match(
'/^(["\']).*\1$/m', trim($param))) {
1621 $replacement = $this->addUniquenessFunctionCall(trim($param));
1626 return implode(
", ",
$result);
1635 private function processPressKey($input)
1638 $input = trim($input);
1639 $this->validateParameterArray($input);
1641 $input = substr($input, 1, strlen($input) - 2);
1648 preg_match_all(
'/[\[][^\]]*?[\]]/', $input, $paramInput);
1649 if (!empty($paramInput)) {
1650 foreach ($paramInput[0] as $param) {
1651 $arrayResult[self::PRESSKEY_ARRAY_ANCHOR_KEY .
$count] =
1652 '[' . trim($this->addUniquenessToParamArray($param)) .
']';
1653 $input = str_replace($param, self::PRESSKEY_ARRAY_ANCHOR_KEY .
$count, $input);
1658 $paramArray = explode(
",", $input);
1659 foreach ($paramArray as $param) {
1661 if (preg_match(
'/^[\s]*(\'.*?\')[\s]*$/', $param)) {
1667 if (substr(trim($param), 0, 1) ===
'\\') {
1673 if (preg_match(
'/^[\s]*(\d+?)[\s]*$/', $param)) {
1678 $replacement = $this->addUniquenessFunctionCall(trim($param));
1685 if (!empty($arrayResult)) {
1686 foreach ($arrayResult as $key =>
$value) {
1699 private function addUniquenessFunctionCall($input)
1701 $output = $this->wrapWithDoubleQuotes($input);
1705 foreach (array_unique($matches[0]) as $match) {
1706 preg_match(
'/\\\\"([\w]+)\\\\"/', $match, $entityMatch);
1711 return preg_replace(
'/(?(?<![\\\\])"" \. )| \. ""/',
"",
$output);
1720 private function wrapWithDoubleQuotes($input)
1722 if ($input ==
null) {
1726 $input = str_replace(
'"',
'\"', $input);
1727 return sprintf(
'"%s"', $input);
1736 private function stripWrappedQuotes($input)
1738 if (empty($input)) {
1741 if (substr($input, 0, 1) ===
'"') {
1742 $input = substr($input, 1);
1744 if (substr($input, -1, 1) ===
'"') {
1745 $input = substr($input, 0, -1);
1756 private function addDollarSign($input)
1758 return sprintf(
"$%s", ltrim($this->stripQuotes($input),
'$'));
1773 private function wrapFunctionCall($actor, $action, ...$args)
1776 $output = sprintf(
"\t\t$%s->%s(", $actor, $action->getType());
1777 for (
$i = 0;
$i < count($args);
$i++) {
1778 if (
null === $args[
$i]) {
1781 if ($args[
$i] ===
"") {
1782 $args[
$i] =
'"' . $args[
$i] .
'"';
1785 if (!is_array($args)) {
1788 $args = $this->resolveAllRuntimeReferences($args);
1789 $args = $this->resolveTestVariable($args, $action->getActionOrigin());
1790 $output .= implode(
", ", array_filter($args,
function(
$value) {
return $value !==
null; })) .
");\n";
1805 private function wrapFunctionCallWithReturnValue($returnVariable, $actor, $action, ...$args)
1808 $output = sprintf(
"\t\t$%s = $%s->%s(", $returnVariable, $actor, $action->getType());
1809 for (
$i = 0;
$i < count($args);
$i++) {
1810 if (
null === $args[
$i]) {
1813 if ($args[
$i] ===
"") {
1814 $args[
$i] =
'"' . $args[
$i] .
'"';
1817 if (!is_array($args)) {
1820 $args = $this->resolveAllRuntimeReferences($args);
1821 $args = $this->resolveTestVariable($args, $action->getActionOrigin());
1822 $output .= implode(
", ", array_filter($args,
function(
$value) {
return $value !==
null; })) .
");\n";
1834 private function resolveRuntimeReference($args, $regex, $func)
1838 foreach ($args as $key => $arg) {
1839 preg_match_all($regex, $arg, $matches);
1840 if (!empty($matches[0])) {
1841 $fullMatch = $matches[0][0];
1842 $refVariable = $matches[1][0];
1846 $outputArg = $this->processQuoteBreaks($fullMatch, $arg,
$replacement);
1847 $newArgs[$key] = $outputArg;
1850 $newArgs[$key] = $arg;
1864 private function resolveAllRuntimeReferences($args)
1866 $runtimeReferenceRegex = [
1867 "/{{_ENV\.([\w]+)}}/" =>
'getenv',
1868 "/{{_CREDS\.([\w]+)}}/" =>
'CredentialStore::getInstance()->getSecret' 1872 foreach ($runtimeReferenceRegex as $regex => $func) {
1873 $argResult = $this->resolveRuntimeReference($argResult, $regex, $func);
1886 private function validateParameterArray($paramArray)
1888 if (substr($paramArray, 0, 1) !=
"[" || substr($paramArray, strlen($paramArray) - 1, 1) !=
"]") {
1889 throw new TestReferenceException(
"parameterArray must begin with `[` and end with `]");
1902 private function resolveValueByType(
$value,
$type)
1908 if (
null ===
$type) {
1911 if (
$type ==
"string") {
1912 return $this->addUniquenessFunctionCall(
$value);
1914 return $this->toBoolean(
$value) ?
"true" :
"false";
1916 return $this->toNumber(
$value);
1918 $this->validateParameterArray(
$value);
1919 return "[" . $this->addUniquenessToParamArray(
$value) .
"]";
1921 return $this->addDollarSign(
$value);
1933 private function toBoolean($inStr)
1935 return boolval($this->stripQuotes($inStr));
1944 private function toNumber($inStr)
1946 $outStr = $this->stripQuotes($inStr);
1947 if (strpos($outStr, localeconv()[
'decimal_point']) ===
false) {
1948 return intval($outStr);
1950 return floatval($outStr);
1960 private function stripQuotes($inStr)
1962 $unquoted = preg_replace(
'/^(\'(.*)\'|"(.*)")$/',
'$2$3', $inStr);
1974 private function validateXmlAttributesMutuallyExclusive($key, $tagName,
$attributes)
2010 if (isset(
$rule[
'excludes']) && in_array($tagName,
$rule[
'excludes'])) {
2020 $this->printRuleErrorToConsole($key, $tagName,
$rule[
'attributes']);
2033 private function printRuleErrorToConsole($key, $tagName,
$attributes)
2038 $message =
'On step with stepKey "' . $key .
'", only one of the attributes: "';
2040 $message .=
'" can be use for action "' . $tagName .
"\".\n";
const ACTION_GROUP_ORIGIN_TEST_REF
elseif(isset( $params[ 'redirect_parent']))
const REQUIRED_ENTITY_REFERENCE
return $annotationToAppend
static getInstance($dir=null, $tests=[], $debug=false)
const ACTION_GROUP_ORIGIN_NAME
generateStepsPhp($actionObjects, $generationScope=TestGenerator::TEST_SCOPE, $actor="I")
const CEST_UNIQUE_FUNCTION
const FUNCTION_CLOSURE_ACTIONS
static createGroupDir($fullPath)
createAllTestFiles($testManifest=null, $testsToIgnore=null)
const PRESSKEY_ARRAY_ANCHOR_KEY
if(!isset($_GET['name'])) $name