diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php index 511c9748f..890083d51 100644 --- a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Objects/ActionObjectTest.php @@ -24,6 +24,8 @@ */ class ActionObjectTest extends MagentoTestCase { + const STUB_PAGE_URL_WITH_NO_ATTRIBUTE = '{{PageObject}}/some/path'; + /** * Before test functionality * @return void @@ -227,16 +229,17 @@ public function testResolveUrl() } /** - * {{PageObject}} should not be replaced and should elicit a warning in console + * {{PageObject}} should not be replaced and should throw an exception! * * @throws /Exception */ public function testResolveUrlWithNoAttribute() { - // Set up mocks + // Given $actionObject = new ActionObject('merge123', 'amOnPage', [ - 'url' => '{{PageObject}}' + 'url' => self::STUB_PAGE_URL_WITH_NO_ATTRIBUTE ]); + $pageObject = new PageObject('PageObject', '/replacement/url.html', 'Test', [], false, "test"); $pageObjectList = ["PageObject" => $pageObject]; $instance = AspectMock::double( @@ -245,20 +248,14 @@ public function testResolveUrlWithNoAttribute() )->make(); // bypass the private constructor AspectMock::double(PageObjectHandler::class, ['getInstance' => $instance]); - // Call the method under test - $actionObject->resolveReferences(); - - // Expect this warning to get generated - TestLoggingUtil::getInstance()->validateMockLogStatement( - "warning", - "page url attribute not found and is required", - ['action' => $actionObject->getType(), 'url' => '{{PageObject}}', 'stepKey' => $actionObject->getStepKey()] - ); - - // Verify + // Expect + $this->expectExceptionMessage('Can not resolve replacements: "{{PageObject}}"'); $expected = [ - 'url' => '{{PageObject}}' + 'url' => self::STUB_PAGE_URL_WITH_NO_ATTRIBUTE ]; + + // When + $actionObject->resolveReferences(); $this->assertEquals($expected, $actionObject->getCustomActionAttributes()); } diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index f377e2fac..4cc955270 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -76,6 +76,9 @@ class ActionObject const ACTION_ATTRIBUTE_USERINPUT = 'userInput'; const ACTION_TYPE_COMMENT = 'comment'; const INVISIBLE_STEP_ACTIONS = ['retrieveEntityField', 'getSecret']; + const REGEX_SINGLE_GROUP = '[\w]+'; + const REGEX_WITH_INDEX = '[\w]+\.[\w\[\]]+'; + const REGEX_WITH_PARAM = '[\w]+\.[\w]+\((?(?!}}).)+\)'; /** * The unique identifier for the action @@ -415,6 +418,14 @@ private function resolveUrlReference() $url = $this->actionAttributes[ActionObject::ACTION_ATTRIBUTE_URL]; $replacement = $this->findAndReplaceReferences(PageObjectHandler::getInstance(), $url); + + $missingReferences = $this->getMissingReferences($replacement); + if (!empty($missingReferences)) { + throw new TestReferenceException( + sprintf('Can not resolve replacements: "%s"', implode('", "', $missingReferences)) + ); + } + if ($replacement) { $this->resolvedCustomAttributes[ActionObject::ACTION_ATTRIBUTE_URL] = $replacement; $allPages = PageObjectHandler::getInstance()->getAllObjects(); @@ -428,6 +439,27 @@ private function resolveUrlReference() } } + /** + * Returns array of missing references + * + * @param string $replacement + * @return array + */ + private function getMissingReferences($replacement): array + { + $matchPatterns = [ + self::REGEX_SINGLE_GROUP, + self::REGEX_WITH_INDEX, + self::REGEX_WITH_PARAM + ]; + + preg_match_all($this->getMustachePattern($matchPatterns), $replacement, $matches); + + return array_filter($matches[1], function ($match) { + return !empty($match) && false === strpos($match, '_ENV.'); + }); + } + /** * Look up the value for EntityDataObjectName.Key and set it as the corresponding attribute in the resolved custom * attributes. @@ -521,10 +553,12 @@ private function stripAndReturnParameters($reference) */ private function findAndReplaceReferences($objectHandler, $inputString) { - //look for parameter area, if so use different regex - $regex = ActionObject::ACTION_ATTRIBUTE_VARIABLE_REGEX_PATTERN; + $matchPatterns = [ + self::REGEX_WITH_INDEX, + self::REGEX_WITH_PARAM + ]; - preg_match_all($regex, $inputString, $matches); + preg_match_all($this->getMustachePattern($matchPatterns), $inputString, $matches); $outputString = $inputString; @@ -722,7 +756,11 @@ private function resolveParameterization($isParameterized, $replacement, $match, */ private function matchParameterReferences($reference, $parameters) { - preg_match_all('/{{[\w.]+}}/', $reference, $varMatches); + $matchPatterns = [ + self::REGEX_SINGLE_GROUP + ]; + + preg_match_all($this->getMustachePattern($matchPatterns), $reference, $varMatches); $varMatches[0] = array_unique($varMatches[0]); $this->checkParameterCount($varMatches[0], $parameters, $reference); @@ -793,6 +831,17 @@ private function checkParameterCount($matches, $parameters, $reference) } } + /** + * Returns Mustache regex pattern + * + * @param array|null $patterns + * @return string + */ + private function getMustachePattern(array $patterns = []): string + { + return '/({{' .implode('}})|({{', $patterns).'}})/'; + } + /** * Returns array of deprecated usages in Action. *