From a92df01d6634e146ce84da82d2d38360bdc4b407 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Mon, 2 Oct 2017 10:06:26 -0500 Subject: [PATCH 01/27] MQE-391: Variable is not correctly resolved when array symbol ([ or ]) is inside $$ signs - Added check for syntax of $data.key[index]$, if so generates line like "$data.getCreatedData('key')['index'] --- .../Util/TestGenerator.php | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 3af4008b2..c4cd5607b 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -806,7 +806,7 @@ private function resolveTestVariable($inputString, $quoteBreak = false) $replaced = false; // Check for Cest-scope variables first, stricter regex match. - preg_match_all("/\\$\\$[\w.]+\\$\\$/", $outputString, $matches); + preg_match_all("/\\$\\$[\w.\[\]]+\\$\\$/", $outputString, $matches); foreach ($matches[0] as $match) { $replacement = null; $variable = $this->stripAndSplitReference($match, '$$'); @@ -816,7 +816,19 @@ private function resolveTestVariable($inputString, $quoteBreak = false) ". Hook persisted entity references must follow \$\$entityMergeKey.field\$\$ format." ); } - $replacement = sprintf("\$this->%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); + preg_match_all("/\[[\w.]+\]/", $variable[1], $arrayMatch); + if (!empty($arrayMatch[0])) { + $variable[1] = str_replace($arrayMatch[0][0], "", $variable[1]); + $arrayMatch[0][0] = trim($arrayMatch[0][0], "[]"); + $replacement = sprintf( + "\$this->%s->getCreatedDataByName('%s')['%s']", + $variable[0], + $variable[1], + $arrayMatch[0][0] + ); + } else { + $replacement = sprintf("\$this->%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); + } if ($quoteBreak) { $replacement = '" . ' . $replacement . ' . "'; } @@ -825,7 +837,7 @@ private function resolveTestVariable($inputString, $quoteBreak = false) } // Check Test-scope variables - preg_match_all("/\\$[\w.]+\\$/", $outputString, $matches); + preg_match_all("/\\$[\w.\[\]]+\\$/", $outputString, $matches); foreach ($matches[0] as $match) { $replacement = null; $variable = $this->stripAndSplitReference($match, '$'); @@ -835,7 +847,19 @@ private function resolveTestVariable($inputString, $quoteBreak = false) ". Test persisted entity references must follow \$entityMergeKey.field\$ format." ); } - $replacement = sprintf("$%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); + preg_match_all("/\[[\w.]+\]/", $variable[1], $arrayMatch); + if (!empty($arrayMatch[0])) { + $variable[1] = str_replace($arrayMatch[0][0], "", $variable[1]); + $arrayMatch[0][0] = trim($arrayMatch[0][0], "[]"); + $replacement = sprintf( + "$%s->getCreatedDataByName('%s')['%s']", + $variable[0], + $variable[1], + $arrayMatch[0][0] + ); + } else { + $replacement = sprintf("$%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); + } if ($quoteBreak) { $replacement = '" . ' . $replacement . ' . "'; } From 05af99129fb2a6c50345df7c19ae231024c66e05 Mon Sep 17 00:00:00 2001 From: KevinBKozan Date: Mon, 2 Oct 2017 13:15:07 -0500 Subject: [PATCH 02/27] MQE-391: Variable is not correctly resolved when array symbol ([ or ]) is inside $$ signs - Reverted most changes and just kept regex change to accomodate []. --- .../Util/TestGenerator.php | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index c4cd5607b..edf11bb15 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -816,19 +816,7 @@ private function resolveTestVariable($inputString, $quoteBreak = false) ". Hook persisted entity references must follow \$\$entityMergeKey.field\$\$ format." ); } - preg_match_all("/\[[\w.]+\]/", $variable[1], $arrayMatch); - if (!empty($arrayMatch[0])) { - $variable[1] = str_replace($arrayMatch[0][0], "", $variable[1]); - $arrayMatch[0][0] = trim($arrayMatch[0][0], "[]"); - $replacement = sprintf( - "\$this->%s->getCreatedDataByName('%s')['%s']", - $variable[0], - $variable[1], - $arrayMatch[0][0] - ); - } else { - $replacement = sprintf("\$this->%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); - } + $replacement = sprintf("\$this->%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); if ($quoteBreak) { $replacement = '" . ' . $replacement . ' . "'; } @@ -847,19 +835,7 @@ private function resolveTestVariable($inputString, $quoteBreak = false) ". Test persisted entity references must follow \$entityMergeKey.field\$ format." ); } - preg_match_all("/\[[\w.]+\]/", $variable[1], $arrayMatch); - if (!empty($arrayMatch[0])) { - $variable[1] = str_replace($arrayMatch[0][0], "", $variable[1]); - $arrayMatch[0][0] = trim($arrayMatch[0][0], "[]"); - $replacement = sprintf( - "$%s->getCreatedDataByName('%s')['%s']", - $variable[0], - $variable[1], - $arrayMatch[0][0] - ); - } else { - $replacement = sprintf("$%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); - } + $replacement = sprintf("$%s->getCreatedDataByName('%s')", $variable[0], $variable[1]); if ($quoteBreak) { $replacement = '" . ' . $replacement . ' . "'; } From bf34adab34fc137cf6626f38739b1e937cd84a19 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Tue, 3 Oct 2017 17:01:34 -0500 Subject: [PATCH 03/27] MQE-377: Implement data persistence through frontend http request. --- .../Persist/Curl/AdminExecutor.php | 20 +- .../Persist/Curl/FrontendExecutor.php | 210 ++++++++++++++++++ .../DataGenerator/Persist/CurlHandler.php | 8 +- 3 files changed, 222 insertions(+), 16 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php index 2fd97eeb3..fe425b9d5 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/AdminExecutor.php @@ -59,12 +59,12 @@ public function __construct() } /** - * Authorize customer on backend. + * Authorize admin on backend. * * @return void * @throws TestFrameworkException */ - public function authorize() + private function authorize() { // Perform GET to backend url so form_key is set $this->transport->write(self::$adminUrl, [], CurlInterface::GET); @@ -85,7 +85,7 @@ public function authorize() } /** - * Init Form Key from response. + * Set Form Key from response. * * @return void */ @@ -114,7 +114,7 @@ public function write($url, $data = [], $method = CurlInterface::POST, $headers $data['form_key'] = $this->formKey; } else { throw new TestFrameworkException( - sprintf('Form key is absent! Url: "%s" Response: "%s"', $url, $this->response) + sprintf('Form key is absent! Url: "%s" Response: "%s"', $apiUrl, $this->response) ); } @@ -135,22 +135,14 @@ public function read($successRegex = null, $returnRegex = null) $this->setFormKey(); if (!empty($successRegex)) { - preg_match( - '/' . preg_quote($successRegex, '/') . '/', - $this->response, - $successMatches - ); + preg_match($successRegex, $this->response, $successMatches); if (empty($successMatches)) { throw new TestFrameworkException("Entity creation was not successful! Response: $this->response"); } } if (!empty($returnRegex)) { - preg_match( - '/' . preg_quote($returnRegex, '/') . '/', - $this->response, - $returnMatches - ); + preg_match($returnRegex, $this->response, $returnMatches); if (!empty($returnMatches)) { return $returnMatches; } diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php new file mode 100644 index 000000000..db945b779 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php @@ -0,0 +1,210 @@ +transport = new CurlTransport(); + $this->customerEmail = $customerEmail; + $this->customerPassword = $customerPassWord; + $this->authorize(); + } + + /** + * Authorize customer on frontend. + * + * @return void + * @throws TestFrameworkException + */ + private function authorize() + { + $url = parent::$baseUrl . 'customer/account/login/'; + $this->transport->write($url); + $this->read(); + + $url = parent::$baseUrl . 'customer/account/loginPost/'; + $data = [ + 'login[username]' => $this->customerEmail, + 'login[password]' => $this->customerPassword, + 'form_key' => $this->formKey, + ]; + $this->transport->write($url, $data, CurlInterface::POST, ['Set-Cookie:' . $this->cookies]); + $response = $this->read(); + if (strpos($response, 'customer/account/login')) { + throw new TestFrameworkException($this->customerEmail . ', cannot be logged in by curl handler!'); + } + } + + /** + * Set Form Key from response. + * + * @return void + */ + private function setFormKey() + { + $str = substr($this->response, strpos($this->response, 'form_key')); + preg_match('/value="(.*)" \/>/', $str, $matches); + if (!empty($matches[1])) { + $this->formKey = $matches[1]; + } + } + + /** + * Set Cookies from response. + * + * @return void + */ + protected function setCookies() + { + preg_match_all('|Set-Cookie: (.*);|U', $this->response, $matches); + if (!empty($matches[1])) { + $this->cookies = implode('; ', $matches[1]); + } + } + + /** + * Send request to the remote server. + * + * @param string $url + * @param array $data + * @param string $method + * @param mixed $headers + * @return void + * @throws TestFrameworkException + */ + public function write($url, $data = [], $method = CurlInterface::POST, $headers = []) + { + if(isset($data['customer_email'])) { + unset($data['customer_email']); + } + if(isset($data['customer_password'])) { + unset($data['customer_password']); + } + $apiUrl = parent::$baseUrl . $url; + if ($this->formKey) { + $data['form_key'] = $this->formKey; + } else { + throw new TestFrameworkException( + sprintf('Form key is absent! Url: "%s" Response: "%s"', $apiUrl, $this->response) + ); + } + $headers = ['Set-Cookie:' . $this->cookies]; + $this->transport->write($apiUrl, str_replace('null', '', http_build_query($data)), $method, $headers); + } + + /** + * Read response from server. + * + * @param string $successRegex + * @param string $returnRegex + * @return string|array + * @throws TestFrameworkException + */ + public function read($successRegex = null, $returnRegex = null) + { + $this->response = $this->transport->read(); + $this->setCookies(); + $this->setFormKey(); + + if (!empty($successRegex)) { + preg_match($successRegex, $this->response, $successMatches); + if (empty($successMatches)) { + throw new TestFrameworkException("Entity creation was not successful! Response: $this->response"); + } + } + + if (!empty($returnRegex)) { + preg_match($returnRegex, $this->response, $returnMatches); + if (!empty($returnMatches)) { + return $returnMatches; + } + } + return $this->response; + } + + /** + * Add additional option to cURL. + * + * @param int $option the CURLOPT_* constants + * @param mixed $value + * @return void + */ + public function addOption($option, $value) + { + $this->transport->addOption($option, $value); + } + + /** + * Close the connection to the server. + * + * @return void + */ + public function close() + { + $this->transport->close(); + } +} diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php index e7d73676e..e8223b245 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\DataGenerator\Persist; use Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl\AdminExecutor; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl\FrontendExecutor; use Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl\WebapiExecutor; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\OperationDefinitionObjectHandler; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; @@ -123,8 +124,11 @@ public function executeRequest($dependentEntities) $executor = new WebapiExecutor($this->storeCode); } elseif ($authorization === 'adminFormKey') { $executor = new AdminExecutor(); - //TODO: add frontend request executor - //} elseif ($authorization === 'customFromKey') { + } elseif ($authorization === 'customerFormKey') { + $executor = new FrontendExecutor( + $this->requestData['customer_email'], + $this->requestData['customer_password'] + ); } if (!$executor) { From abf8b2335aab85e77df09e390ac8919d51fb0897 Mon Sep 17 00:00:00 2001 From: Tom Reece Date: Tue, 10 Oct 2017 14:37:12 -0500 Subject: [PATCH 04/27] MQE-431: Automatically check for copyright at the top of every framework file - Add copyright-check, a bash script to check for the Magento copyright in all tracked files - Fix various missing or incorrect copyrights --- bin/copyright-check | 27 +++++++++++++++++++ bin/copyright-ignore.txt | 1 + .../Code/Reader/ClassReaderInterface.php | 3 +-- .../Config/Converter/Dom/Flat.php | 2 +- .../Config/FileResolver/Mask.php | 2 +- .../Data/Argument/Interpreter/ArrayType.php | 2 +- .../Handlers/DataObjectHandler.php | 4 +++ .../DataGenerator/etc/dataOperation.xsd | 7 +++++ .../DataGenerator/etc/dataProfileSchema.xsd | 7 +++++ .../Module/MagentoRestDriver.php | 5 ++++ .../Module/MagentoSequence.php | 5 ++++ .../Module/MagentoWebDriver.php | 5 ++++ .../Page/etc/PageObject.xsd | 6 +++++ .../Page/etc/SectionObject.xsd | 6 +++++ .../Step/Backend/AdminStep.php | 5 ++++ .../Test/Config/Converter/Dom/Flat.php | 3 ++- .../Test/Util/TestEntityExtractor.php | 6 ++--- .../Test/etc/sampleActionGroup.xml | 6 +++++ .../Test/etc/sampleCest.xml | 6 +++++ .../Test/etc/testSchema.xsd | 7 +++++ .../Util/ModuleResolver/SequenceSorter.php | 5 ++++ .../SequenceSorterInterface.php | 5 ++++ .../Util/Protocol/CurlInterface.php | 2 +- .../Util/Protocol/CurlTransport.php | 2 +- .../FunctionalTestingFramework/Util/msq.php | 5 ++++ .../XmlParser/ParserInterface.php | 5 ++++ .../XmlParser/SectionParser.php | 5 ++++ 27 files changed, 132 insertions(+), 12 deletions(-) create mode 100755 bin/copyright-check create mode 100644 bin/copyright-ignore.txt diff --git a/bin/copyright-check b/bin/copyright-check new file mode 100755 index 000000000..3c1b28c34 --- /dev/null +++ b/bin/copyright-check @@ -0,0 +1,27 @@ +#!/bin/bash + +IGNORE_PATH='bin/copyright-ignore.txt' +FILE_EXTENSIONS_TO_CHECK='.php\|.xml\|.xsd' + +RESULT='' + +# Iterate through all php, xml, and xsd files that are part of the Git repo +# (excluding any matching lines from the ignore file) +for i in `git ls-tree --full-tree -r --name-only HEAD | grep $FILE_EXTENSIONS_TO_CHECK | grep -v -f $IGNORE_PATH` +do + if echo `cat $i` | grep -q -v "Copyright © Magento, Inc. All rights reserved."; then + # Copyright is missing + RESULT+="$i\n" + fi +done + +if [[ ! -z $RESULT ]]; then + printf "\nTHE FOLLOWING FILES ARE MISSING THE MAGENTO COPYRIGHT:\n\n" + printf " Copyright © Magento, Inc. All rights reserved.\n" + printf " See COPYING.txt for license details.\n\n" + printf "$RESULT\n" + exit 1 +fi + +# Success! +exit 0 diff --git a/bin/copyright-ignore.txt b/bin/copyright-ignore.txt new file mode 100644 index 000000000..02bc262b2 --- /dev/null +++ b/bin/copyright-ignore.txt @@ -0,0 +1 @@ +dev/tests/static/Magento/Sniffs/Annotations/ diff --git a/src/Magento/FunctionalTestingFramework/Code/Reader/ClassReaderInterface.php b/src/Magento/FunctionalTestingFramework/Code/Reader/ClassReaderInterface.php index 21472847c..ff49f1d37 100644 --- a/src/Magento/FunctionalTestingFramework/Code/Reader/ClassReaderInterface.php +++ b/src/Magento/FunctionalTestingFramework/Code/Reader/ClassReaderInterface.php @@ -1,7 +1,6 @@ + + diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd index c0494c911..f42a11c40 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd @@ -1,4 +1,11 @@ + + diff --git a/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php b/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php index bae3c50f7..790626629 100644 --- a/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php +++ b/src/Magento/FunctionalTestingFramework/Module/MagentoRestDriver.php @@ -1,4 +1,9 @@ + diff --git a/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd b/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd index e0c421dcb..4fdeba173 100644 --- a/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd +++ b/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd @@ -1,4 +1,10 @@ + diff --git a/src/Magento/FunctionalTestingFramework/Step/Backend/AdminStep.php b/src/Magento/FunctionalTestingFramework/Step/Backend/AdminStep.php index 8284de253..f68ad1074 100644 --- a/src/Magento/FunctionalTestingFramework/Step/Backend/AdminStep.php +++ b/src/Magento/FunctionalTestingFramework/Step/Backend/AdminStep.php @@ -1,4 +1,9 @@ + diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml b/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml index 2ae9e1260..c6966af5a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml +++ b/src/Magento/FunctionalTestingFramework/Test/etc/sampleCest.xml @@ -1,4 +1,10 @@ + diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index 58f455a6e..dedaca0c2 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -1,4 +1,11 @@ + + diff --git a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver/SequenceSorter.php b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver/SequenceSorter.php index c8a0d7530..10c285923 100644 --- a/src/Magento/FunctionalTestingFramework/Util/ModuleResolver/SequenceSorter.php +++ b/src/Magento/FunctionalTestingFramework/Util/ModuleResolver/SequenceSorter.php @@ -1,4 +1,9 @@ Date: Tue, 10 Oct 2017 16:08:50 -0500 Subject: [PATCH 05/27] MQE-431: Automatically check for copyright at the top of every framework file - Add composer-git-hooks dependency --- .gitignore | 1 + composer.json | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8e2330435..47a7c9c09 100755 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ vendor/* .env _generated AcceptanceTester.php +cghooks.lock diff --git a/composer.json b/composer.json index 2bd7d5aea..65c607324 100755 --- a/composer.json +++ b/composer.json @@ -11,11 +11,17 @@ }, "require-dev": { "squizlabs/php_codesniffer": "1.5.3", - "sebastian/phpcpd": "~3.0" + "sebastian/phpcpd": "~3.0", + "brainmaestro/composer-git-hooks": "^2.3" }, "autoload": { "psr-4": { "Magento\\FunctionalTestingFramework\\": ["src/Magento/FunctionalTestingFramework"] } + }, + "extra": { + "hooks": { + "pre-push": "bin/copyright-check" + } } } From a0f53f8b271f5bb8f8132b18a6392dfee5ce9095 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Tue, 10 Oct 2017 16:39:48 -0500 Subject: [PATCH 06/27] MQE-377: Fixed code sniffer errors. --- .../DataGenerator/Persist/Curl/FrontendExecutor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php index db945b779..4282cf4fc 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php @@ -138,10 +138,10 @@ protected function setCookies() */ public function write($url, $data = [], $method = CurlInterface::POST, $headers = []) { - if(isset($data['customer_email'])) { + if (isset($data['customer_email'])) { unset($data['customer_email']); } - if(isset($data['customer_password'])) { + if (isset($data['customer_password'])) { unset($data['customer_password']); } $apiUrl = parent::$baseUrl . $url; From b18fbc2b6b0e014e7bbefad7ad35a60f467ddb6a Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Wed, 11 Oct 2017 14:44:52 -0500 Subject: [PATCH 07/27] MQE-345:[DevOps] [Customizability] Create suite schema declaration and supporting interpretation - add new suite objects, handler, parser classes - alter di.xml file to include new suite parsing - add new resolver for root level dir - change cest objects and test generators to fit with suite design --- etc/di.xml | 32 +++ .../Config/FileResolver/Root.php | 27 ++ .../Objects/EntityDataObject.php | 2 +- .../ObjectManager/ObjectHandlerInterface.php | 4 +- .../Suite/Handlers/SuiteObjectHandler.php | 248 ++++++++++++++++++ .../Suite/Objects/SuiteObject.php | 150 +++++++++++ .../Suite/Parsers/SuiteDataParser.php | 38 +++ .../Suite/SuiteGenerator.php | 125 +++++++++ .../Suite/etc/sampleSuite.xml | 16 ++ .../Suite/etc/suiteSchema.xsd | 56 ++++ .../Test/Handlers/CestObjectHandler.php | 43 +++ .../Test/Objects/CestObject.php | 9 + .../Test/Objects/TestObject.php | 13 +- .../Test/Util/ActionMergeUtil.php | 5 + .../Test/Util/TestObjectExtractor.php | 2 +- .../Util/Filesystem/DirSetupUtil.php | 47 ++++ .../Util/TestGenerator.php | 83 +++--- 17 files changed, 842 insertions(+), 58 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml create mode 100644 src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd create mode 100644 src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php diff --git a/etc/di.xml b/etc/di.xml index b3be5638d..02c13ad01 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -309,4 +309,36 @@ Magento\FunctionalTestingFramework\Config\Reader\ActionGroupData + + + + + + Magento\FunctionalTestingFramework\Suite\Config\SuiteData + + + + + Magento\FunctionalTestingFramework\Config\Reader\SuiteData + + + + + Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd + + + + + Magento\FunctionalTestingFramework\Config\FileResolver\Root + Magento\FunctionalTestingFramework\Config\Converter + Magento\FunctionalTestingFramework\Config\SchemaLocator\SuiteData + + name + name + name + + *.xml + _suite + + diff --git a/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php new file mode 100644 index 000000000..abb59e6c1 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php @@ -0,0 +1,27 @@ +id) * * @param string $dataKey - * @return array|null + * @return string|null */ public function getVarReference($dataKey) { diff --git a/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectHandlerInterface.php b/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectHandlerInterface.php index 1c2e76b2d..1bc79cbbb 100644 --- a/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectHandlerInterface.php +++ b/src/Magento/FunctionalTestingFramework/ObjectManager/ObjectHandlerInterface.php @@ -21,10 +21,10 @@ public static function getInstance(); /** * Function to return a single object by name * - * @param string $jsonDefitionName + * @param string $objectName * @return object */ - public function getObject($jsonDefitionName); + public function getObject($objectName); /** * Function to return all objects the handler is responsible for diff --git a/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php new file mode 100644 index 000000000..11921a51e --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php @@ -0,0 +1,248 @@ +initSuiteData(); + } + + return self::$SUITE_OBJECT_HANLDER_INSTANCE; + } + + /** + * Function to return a single suite object by name + * + * @param string $objectName + * @return SuiteObject + */ + public function getObject($objectName) + { + if (!array_key_exists($objectName, $this->suiteObjects)) { + trigger_error("Suite ${objectName} is not defined.", E_USER_ERROR); + } + return $this->suiteObjects[$objectName]; + } + + /** + * Function to return all objects the handler is responsible for + * + * @return array + */ + public function getAllObjects() + { + return $this->suiteObjects; + } + + private function initSuiteData() + { + $suiteDataParser = ObjectManagerFactory::getObjectManager()->create(SuiteDataParser::class); + $this->suiteObjects = $this->parseSuiteDataIntoObjects($suiteDataParser->readSuiteData()); + } + + private function parseSuiteDataIntoObjects($parsedSuiteData) + { + $suiteObjects = []; + foreach ($parsedSuiteData[self::SUITE_TAG_NAME] as $parsedSuiteName => $parsedSuite) { + $includeCests = []; + $excludeCests = []; + + $groupCestsToInclude = $parsedSuite[self::INCLUDE_TAG_NAME][0] ?? []; + $groupCestsToExclude = $parsedSuite[self::EXCLUDE_TAG_NAME][0] ?? []; + + if (array_key_exists(self::CEST_TAG_NAME, $groupCestsToInclude)) { + $includeCests = $includeCests + $this->extractRelevantTests($groupCestsToInclude[self::CEST_TAG_NAME]); + } + + if (array_key_exists(self::CEST_TAG_NAME, $groupCestsToExclude)) { + $excludeCests = $excludeCests + $this->extractRelevantTests($groupCestsToExclude[self::CEST_TAG_NAME]); + } + + $includeCests = $includeCests + $this->extractGroups($groupCestsToInclude); + $excludeCests = $excludeCests + $this->extractGroups($groupCestsToExclude); + + + // get tests by path (dir or file) + if (array_key_exists(self::MODULE_TAG_NAME, $groupCestsToInclude)) { + $includeCests = array_merge( + $includeCests, + $this->extractModuleAndFiles($groupCestsToInclude[self::MODULE_TAG_NAME]) + ); + } + + if (array_key_exists(self::MODULE_TAG_NAME, $groupCestsToExclude)) { + $excludeCests = array_merge( + $excludeCests, + $this->extractModuleAndFiles($groupCestsToExclude[self::MODULE_TAG_NAME]) + ); + } + + // add all cests if include cests is completely empty + if (empty($includeCests)) { + $includeCests = CestObjectHandler::getInstance()->getAllObjects(); + } + + $suiteObjects[$parsedSuiteName] = new SuiteObject($parsedSuiteName, $includeCests, $excludeCests); + } + + return $suiteObjects; + } + + private function extractRelevantTests($suiteTestData) + { + $relevantCests = []; + foreach ($suiteTestData as $cestName => $cestInfo) { + $relevantCest = CestObjectHandler::getInstance()->getObject($cestName); + + if (array_key_exists(self::CEST_TAG_TEST_ATTRIBUTE, $cestInfo)) { + $relevantTest = $relevantCest->getTests()[$cestInfo[self::CEST_TAG_TEST_ATTRIBUTE]] ?? null; + + if (!$relevantTest) { + trigger_error( + "Test " . + $cestInfo[self::CEST_TAG_NAME_ATTRIBUTE] . + " does not exist.", + E_USER_NOTICE + ); + continue; + } + + $relevantCests[$cestName] = new CestObject( + $relevantCest->getName(), + $relevantCest->getAnnotations(), + [$relevantTest->getName() => $relevantTest], + $relevantCest->getHooks() + ); + } else { + $relevantCests[$cestName] = $relevantCest; + } + } + + return $relevantCests; + } + + private function extractGroups($suiteData) + { + $cestsByGroup = []; + // get tests by group + if (array_key_exists(self::GROUP_TAG_NAME, $suiteData)) { + //loop groups and add to the groupCests + foreach ($suiteData[self::GROUP_TAG_NAME] as $groupName => $groupVal) { + $cestsByGroup = $cestsByGroup + CestObjectHandler::getInstance()->getCestsByGroup($groupName); + } + } + + return $cestsByGroup; + } + + private function extractModuleAndFiles($suitePathData) + { + $cestsByModule = []; + foreach ($suitePathData as $moduleName => $fileInfo) { + if (empty($fileInfo)) { + $cestsByModule = array_merge($cestsByModule, $this->resolveModulePathCestNames($moduleName)); + } else { + $cestObj = $this->resolveFilePathCestName($fileInfo[self::MODULE_TAG_FILE_ATTRIBUTE], $moduleName); + $cestsByModule[$cestObj->getName()] = $cestObj; + } + } + + return $cestsByModule; + } + + private function resolveFilePathCestName($filename, $moduleName = null) + { + $filepath = $filename; + if (!strstr($filepath, DIRECTORY_SEPARATOR)) { + $filepath = TESTS_MODULE_PATH . + DIRECTORY_SEPARATOR . + $moduleName . + DIRECTORY_SEPARATOR . + 'Cest' . + DIRECTORY_SEPARATOR . + $filename; + } + + if (!file_exists($filepath)) { + throw new Exception("Could not find file ${filename}"); + } + $xml = simplexml_load_file($filepath); + $cestName = (string)$xml->cest->attributes()->name; + + return CestObjectHandler::getInstance()->getObject($cestName); + } + + private function resolveModulePathCestNames($moduleName) + { + $cestObjects = []; + $xmlFiles = glob( + TESTS_MODULE_PATH . + DIRECTORY_SEPARATOR . + $moduleName . + DIRECTORY_SEPARATOR . + 'Cest' . + DIRECTORY_SEPARATOR . + '*.xml' + ); + + foreach ($xmlFiles as $xmlFile) { + $cestObj = $this->resolveFilePathCestName($xmlFile); + $cestObjects[$cestObj->getName()] = $cestObj; + } + + return $cestObjects; + } +} \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php new file mode 100644 index 000000000..1da676ebf --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php @@ -0,0 +1,150 @@ +name = $name; + $this->includeCests = $includeCests; + $this->excludeCests = $excludeCests; + } + + /** + * Getter for suite name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns an array of Cest Objects based on specifications in exclude and include arrays. + * + * @return array + */ + public function getCests() + { + return $this->resolveCests($this->includeCests, $this->excludeCests); + } + + /** + * Takes an array of Cest Objects to include and an array of Cest Objects to exlucde. Loops through each Cest + * and determines any overlapping tests. Returns a resulting array of Cest Objects based on this logic. Exclusion is + * preferred to exclusiong (i.e. a test is specified in both include and exclude, it will be excluded). + * + * @param array $includeCests + * @param array $excludeCests + * @return array + */ + private function resolveCests($includeCests, $excludeCests) + { + $finalCestList = $includeCests; + $matchingCests = array_intersect(array_keys($includeCests), array_keys($excludeCests)); + + // filter out tests within cests here and any overlap + foreach ($matchingCests as $cestName) { + $testNamesForExclusion = array_keys($excludeCests[$cestName]->getTests()); + $relevantTestNames = array_diff(array_keys($includeCests[$cestName]->getTests()), $testNamesForExclusion); + + if (empty($relevantTestNames)) { + unset($finalCestList[$cestName]); + continue; + } + + /** @var CestObject $tempCestObj */ + $tempCestObj = $includeCests[$cestName]; + $finalCestList[$tempCestObj->getName()] = new CestObject( + $tempCestObj->getName(), + $tempCestObj->getAnnotations(), + $this->resolveTests($relevantTestNames, $includeCests[$cestName]->getTests()), + $tempCestObj->getHooks() + ); + } + + if (empty($finalCestList)) { + trigger_error( + "Current suite configuration for " . + $this->name . " contains no cests.", + E_USER_WARNING + ); + } + + return $finalCestList; + } + + /** + * Takes an array of test names to be extracted from an array of test objects. Returns the resulting array of test + * objects contained within the applicable test names. + * + * @param array $relevantTestNames + * @param array $tests + * @return array + */ + private function resolveTests($relevantTestNames, $tests) + { + $relevantTests = []; + foreach ($relevantTestNames as $testName) { + $relevantTests[$testName] = $tests[$testName]; + } + + return $relevantTests; + } + + /** + * Getter for before hooks. + */ + public function getBeforeHook() + { + //TODO implement getter for before hooks + } + + /** + * Getter for after hooks. + */ + public function getAfterHook() + { + //TODO implement getter for after hooks + } +} \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php b/src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php new file mode 100644 index 000000000..0c8446123 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php @@ -0,0 +1,38 @@ +suiteData = $suiteData; + } + + /** + * Returns an array of data based on *Cest.xml files + * + * @return array + */ + public function readSuiteData() + { + return $this->suiteData->get(); + } +} \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php new file mode 100644 index 000000000..62eeccea4 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -0,0 +1,125 @@ +getObject($suiteName); + $relativePath = '_generated' . DIRECTORY_SEPARATOR . $suiteName; + $fullPath = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . $relativePath; + + DirSetupUtil::createGroupDir($fullPath); + $this->generateGroupFile($fullPath); + $this->generateRelevantGroupTests($suiteName, $suite->getCests()); + $this->appendGroupEntryToConfig($suiteName, $fullPath); + print "Suite ${suiteName} generated to ${relativePath}.\n"; + } + + /** + * Method which will be needed for adding preconditions and creating a corresponding group file for codeception. + */ + private function generateGroupFile() + { + // generate group file here + } + + /** + * Function which accepts a suite name and suite path and appends a new group entry to the codeception.yml.dist + * file in order to register the set of tests as a new group. + * + * @param string $suiteName + * @param string $suitePath + */ + private function appendGroupEntryToConfig($suiteName, $suitePath) + { + $configYmlPath = dirname(dirname(TESTS_BP)) . DIRECTORY_SEPARATOR; + $configYmlFile = $configYmlPath . self::YAML_CODECEPTION_CONFIG_FILENAME; + $defaultConfigYmlFile = $configYmlPath . self::YAML_CODECEPTION_DIST_FILENAME; + $relativeSuitePath = substr($suitePath, strlen(dirname(dirname(TESTS_BP))) + 1); + + $ymlContents = null; + if (file_exists($configYmlFile)) { + $ymlContents = file_get_contents($configYmlFile); + } else { + $ymlContents = file_get_contents($defaultConfigYmlFile); + } + + $ymlArray = Yaml::parse($ymlContents) ?? []; + + if (!array_key_exists('group', $ymlArray)) { + $ymlArray['group']= []; + } + + $ymlArray['group'][$suiteName] = $relativeSuitePath; + $ymlText = self::YAML_COPYRIGHT_TEXT . Yaml::dump($ymlArray, 10); + file_put_contents($configYmlFile, $ymlText); + } + + /** + * Function which takes a string which is the desired output directory (under _generated) and an array of cests + * relevant to the suite to be generated. The function takes this information and creates a new instance of the test + * generator which is then called to create all the cest files for the suite. + * + * @param string $path + * @param array $cests + */ + private function generateRelevantGroupTests($path, $cests) + { + $testGenerator = TestGenerator::getInstance($path, $cests); + $testGenerator->createAllCestFiles(); + } +} \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml b/src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml new file mode 100644 index 000000000..d59828bd5 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd b/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd new file mode 100644 index 000000000..6984c9b45 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php index 0bb21897a..e721f5c5a 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php @@ -63,6 +63,11 @@ private function __construct() */ public function getObject($cestName) { + if (!array_key_exists($cestName, $this->cests)) { + trigger_error("Cest ${cestName} not defined in xml.", E_USER_ERROR); + return null; + } + return $this->cests[$cestName]; } @@ -76,6 +81,44 @@ public function getAllObjects() return $this->cests; } + /** + * Returns tests and cests tagged with the group name passed to the method. + * + * @param string $groupName + * @return array + */ + public function getCestsByGroup($groupName) + { + $relevantCests = []; + foreach ($this->cests as $cest) { + /** @var CestObject $cest */ + if (in_array($groupName, $cest->getAnnotationByName('group'))) { + $relevantCests[$cest->getName()] = $cest; + continue; + } + + $relevantTests = []; + // extract relevant tests here + foreach ($cest->getTests() as $test) { + if (in_array($groupName, $test->getAnnotationByName('group'))) { + $relevantTests[$test->getName()] = $test; + continue; + } + } + + if (!empty($relevantTests)) { + $relevantCests[$cest->getName()] = new CestObject( + $cest->getName(), + $cest->getAnnotations(), + $relevantTests, + $cest->getHooks() + ); + } + } + + return $relevantCests; + } + /** * This method reads all Cest.xml files into objects and stores them in an array for future access. * diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php index 5b7018657..a1c644bab 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php @@ -74,6 +74,15 @@ public function getAnnotations() return $this->annotations; } + public function getAnnotationByName($name) + { + if (array_key_exists($name, $this->annotations)) { + return $this->annotations[$name]; + } + + return []; + } + /** * Returns tests. * diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 97865adb1..4eaacdd1f 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -15,10 +15,6 @@ */ class TestObject { - const STEP_MISSING_ERROR_MSG = - "Merge Error - Step could not be found in either TestXML or DeltaXML. - \tTest = '%s'\tTestStep='%s'\tLinkedStep'%s'"; - /** * Name of the test * @@ -82,6 +78,15 @@ public function getAnnotations() return $this->annotations; } + public function getAnnotationByName($name) + { + if (array_key_exists($name, $this->annotations)) { + return $this->annotations[$name]; + } + + return []; + } + /** * Getter for the custom data * @return array|null diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php index ad38df07f..391674650 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php @@ -6,6 +6,7 @@ namespace Magento\FunctionalTestingFramework\Test\Util; +use Magento\FunctionalTestingFramework\Exceptions\XmlException; use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; @@ -14,6 +15,10 @@ */ class ActionMergeUtil { + const STEP_MISSING_ERROR_MSG = + "Merge Error - Step could not be found in either TestXML or DeltaXML. + \tTest = '%s'\tTestStep='%s'\tLinkedStep'%s'"; + const WAIT_ATTR = 'timeout'; const WAIT_ACTION_NAME = 'waitForPageLoad'; const WAIT_ACTION_SUFFIX = 'WaitForPageLoad'; diff --git a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php index 2948059e2..6e7fcb508 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php +++ b/src/Magento/FunctionalTestingFramework/Test/Util/TestObjectExtractor.php @@ -75,7 +75,7 @@ public function extractTestData($cestTestData) $testAnnotations = $this->annotationExtractor->extractAnnotations($testData[self::TEST_ANNOTATIONS]); } - $testObjects[] = new TestObject( + $testObjects[$testName] = new TestObject( $testName, $this->actionObjectExtractor->extractActions($testActions), $testAnnotations, diff --git a/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php new file mode 100644 index 000000000..41462866b --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php @@ -0,0 +1,47 @@ +valid()) { + $path = $directory . DIRECTORY_SEPARATOR . $it->getFilename(); + if ($it->isDir()) { + self::rmDirRecursive($path); + } else { + unlink($path); + } + + $it->next(); + } + + rmdir($directory); + } +} \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index c3e436f3b..aa8c9e803 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -6,19 +6,20 @@ namespace Magento\FunctionalTestingFramework\Util; -use FilesystemIterator; use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject; use Magento\FunctionalTestingFramework\Exceptions\TestReferenceException; use Magento\FunctionalTestingFramework\Test\Handlers\CestObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\CestObject; -use RecursiveDirectoryIterator; +use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; class TestGenerator { const REQUIRED_ENTITY_REFERENCE = 'createDataKey'; + const GENERATED_DIR = '_generated'; + /** * Path to the export dir. @@ -28,72 +29,50 @@ class TestGenerator private $exportDirectory; /** - * Test generator. + * Export dir name. * - * @var TestGenerator + * @var string */ - private static $testGenerator; + private $exportDirName; /** - * TestGenerator constructor. - * @param string $exportDir + * Array of CestObjects to be generated + * + * @var array */ - private function __construct($exportDir) - { - // private constructor for singleton - $this->exportDirectory = $exportDir; - } + private $cests; /** - * Method used to clean export dir if needed and create new empty export dir. - * - * @return void + * TestGenerator constructor. + * @param string $exportDir */ - private function setupExportDir() + private function __construct($exportDir, $cests) { - if (file_exists($this->exportDirectory)) { - $this->rmDirRecursive($this->exportDirectory); - } - - mkdir($this->exportDirectory, 0777, true); + // private constructor for factory + $this->exportDirName = $exportDir ?? self::GENERATED_DIR; + $this->exportDirectory = rtrim(TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . + self::GENERATED_DIR . DIRECTORY_SEPARATOR . $exportDir, DIRECTORY_SEPARATOR); + $this->cests = $cests; } /** - * Takes a directory path and recursively deletes all files and folders. + * Singleton method to retrieve Test Generator * - * @param string $directory - * @return void + * @return TestGenerator */ - private function rmdirRecursive($directory) + public static function getInstance($dir = null, $cests = null) { - $it = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS); - - while ($it->valid()) { - $path = $directory . DIRECTORY_SEPARATOR . $it->getFilename(); - if ($it->isDir()) { - $this->rmDirRecursive($path); - } else { - unlink($path); - } - - $it->next(); - } - - rmdir($directory); + return new TestGenerator($dir, $cests); } /** - * Singleton method to retrieve Test Generator + * Returns the absolute path to the test export director for the generator instance. * - * @return TestGenerator + * @return string */ - public static function getInstance() + public function getExportDir() { - if (!self::$testGenerator) { - self::$testGenerator = new TestGenerator(TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . "_generated"); - } - - return self::$testGenerator; + return $this->exportDirectory; } /** @@ -103,7 +82,11 @@ public static function getInstance() */ private function loadAllCestObjects() { - return CestObjectHandler::getInstance()->getAllObjects(); + if ($this->cests === null) { + return CestObjectHandler::getInstance()->getAllObjects(); + } + + return $this->cests; } /** @@ -136,7 +119,7 @@ private function createCestFile($cestPhp, $filename) */ public function createAllCestFiles() { - $this->setupExportDir(); + DirSetupUtil::createGroupDir($this->exportDirectory); $cestPhpArray = $this->assembleAllCestPhp(); foreach ($cestPhpArray as $cestPhpFile) { @@ -165,7 +148,7 @@ private function assembleCestPhp($cestObject) } $cestPhp = "exportDirName ."\Backend;\n\n"; $cestPhp .= $usePhp; $cestPhp .= $classAnnotationsPhp; $cestPhp .= sprintf("class %s\n", $className); From db0a41ed9b73379c5a5040450f28fd9edcc9fc17 Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Wed, 11 Oct 2017 16:29:20 -0500 Subject: [PATCH 08/27] MQE-345:[DevOps] [Customizability] Create suite schema declaration and supporting interpretation - fix for codesniff tests --- .../Persist/Curl/FrontendExecutor.php | 4 +- .../Suite/Handlers/SuiteObjectHandler.php | 49 ++++++++++++++++++- .../Suite/Objects/SuiteObject.php | 9 ++-- .../Suite/Parsers/SuiteDataParser.php | 2 +- .../Suite/SuiteGenerator.php | 7 ++- .../Test/Objects/CestObject.php | 6 +++ .../Test/Objects/TestObject.php | 6 +++ .../Util/Filesystem/DirSetupUtil.php | 3 +- .../Util/TestGenerator.php | 11 +++-- 9 files changed, 83 insertions(+), 14 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php index 4282cf4fc..b6f448dc0 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/Curl/FrontendExecutor.php @@ -132,7 +132,7 @@ protected function setCookies() * @param string $url * @param array $data * @param string $method - * @param mixed $headers + * @param array $headers * @return void * @throws TestFrameworkException */ @@ -190,7 +190,7 @@ public function read($successRegex = null, $returnRegex = null) * Add additional option to cURL. * * @param int $option the CURLOPT_* constants - * @param mixed $value + * @param int|string|bool|array $value * @return void */ public function addOption($option, $value) diff --git a/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php index 11921a51e..6cc754d13 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php @@ -43,6 +43,9 @@ class SuiteObjectHandler implements ObjectHandlerInterface */ private $suiteObjects; + /** + * SuiteObjectHandler constructor. + */ private function __construct() { // empty constructor @@ -87,12 +90,23 @@ public function getAllObjects() return $this->suiteObjects; } + /** + * Method to parse all suite data xml into objects. + * + * @return void + */ private function initSuiteData() { $suiteDataParser = ObjectManagerFactory::getObjectManager()->create(SuiteDataParser::class); $this->suiteObjects = $this->parseSuiteDataIntoObjects($suiteDataParser->readSuiteData()); } + /** + * Takes an array of parsed xml and converts into an array of suite objects. + * + * @param array $parsedSuiteData + * @return array + */ private function parseSuiteDataIntoObjects($parsedSuiteData) { $suiteObjects = []; @@ -114,7 +128,6 @@ private function parseSuiteDataIntoObjects($parsedSuiteData) $includeCests = $includeCests + $this->extractGroups($groupCestsToInclude); $excludeCests = $excludeCests + $this->extractGroups($groupCestsToExclude); - // get tests by path (dir or file) if (array_key_exists(self::MODULE_TAG_NAME, $groupCestsToInclude)) { $includeCests = array_merge( @@ -141,6 +154,12 @@ private function parseSuiteDataIntoObjects($parsedSuiteData) return $suiteObjects; } + /** + * Takes an array of Cests/Tests and resolves names into corresponding Cest/Test objects. + * + * @param array $suiteTestData + * @return array + */ private function extractRelevantTests($suiteTestData) { $relevantCests = []; @@ -174,6 +193,12 @@ private function extractRelevantTests($suiteTestData) return $relevantCests; } + /** + * Takes an array of group names and resolves to Cest/Test objects. + * + * @param array $suiteData + * @return array + */ private function extractGroups($suiteData) { $cestsByGroup = []; @@ -188,6 +213,12 @@ private function extractGroups($suiteData) return $cestsByGroup; } + /** + * Takes an array of modules/files and resolves to an array of cest objects. + * + * @param array $suitePathData + * @return array + */ private function extractModuleAndFiles($suitePathData) { $cestsByModule = []; @@ -203,6 +234,14 @@ private function extractModuleAndFiles($suitePathData) return $cestsByModule; } + /** + * Takes a filepath (and optionally a module name) and resolves to a cest object. + * + * @param string $filename + * @param null $moduleName + * @return CestObject + * @throws Exception + */ private function resolveFilePathCestName($filename, $moduleName = null) { $filepath = $filename; @@ -225,6 +264,12 @@ private function resolveFilePathCestName($filename, $moduleName = null) return CestObjectHandler::getInstance()->getObject($cestName); } + /** + * Takes a single module name and resolves to an array of cests contained within specified module. + * + * @param string $moduleName + * @return array + */ private function resolveModulePathCestNames($moduleName) { $cestObjects = []; @@ -245,4 +290,4 @@ private function resolveModulePathCestNames($moduleName) return $cestObjects; } -} \ No newline at end of file +} diff --git a/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php index 1da676ebf..c6fc09216 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php @@ -19,7 +19,6 @@ class SuiteObject */ private $name; - /** * Array of Cests to include for the suite. * @@ -27,7 +26,6 @@ class SuiteObject */ private $includeCests = []; - /** * Array of Cests to exclude for the suite. * @@ -35,7 +33,6 @@ class SuiteObject */ private $excludeCests = []; - /** * SuiteObject constructor. * @param string $name @@ -134,6 +131,8 @@ private function resolveTests($relevantTestNames, $tests) /** * Getter for before hooks. + * + * @return void */ public function getBeforeHook() { @@ -142,9 +141,11 @@ public function getBeforeHook() /** * Getter for after hooks. + * + * @return void */ public function getAfterHook() { //TODO implement getter for after hooks } -} \ No newline at end of file +} diff --git a/src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php b/src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php index 0c8446123..2fb2031dc 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Parsers/SuiteDataParser.php @@ -35,4 +35,4 @@ public function readSuiteData() { return $this->suiteData->get(); } -} \ No newline at end of file +} diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index 62eeccea4..659b74d4b 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -55,6 +55,7 @@ public static function getInstance() * yml configuration for group run. * * @param string $suiteName + * @return void */ public function generateSuite($suiteName) { @@ -71,6 +72,8 @@ public function generateSuite($suiteName) /** * Method which will be needed for adding preconditions and creating a corresponding group file for codeception. + * + * @return void */ private function generateGroupFile() { @@ -83,6 +86,7 @@ private function generateGroupFile() * * @param string $suiteName * @param string $suitePath + * @return void */ private function appendGroupEntryToConfig($suiteName, $suitePath) { @@ -116,10 +120,11 @@ private function appendGroupEntryToConfig($suiteName, $suitePath) * * @param string $path * @param array $cests + * @return void */ private function generateRelevantGroupTests($path, $cests) { $testGenerator = TestGenerator::getInstance($path, $cests); $testGenerator->createAllCestFiles(); } -} \ No newline at end of file +} diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php index a1c644bab..81c67ff70 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/CestObject.php @@ -74,6 +74,12 @@ public function getAnnotations() return $this->annotations; } + /** + * Returns the value(s) of an annotation by a specific name such as group + * + * @param string $name + * @return array + */ public function getAnnotationByName($name) { if (array_key_exists($name, $this->annotations)) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php index 4eaacdd1f..6ccf23172 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php @@ -78,6 +78,12 @@ public function getAnnotations() return $this->annotations; } + /** + * Method to return the value(s) of a corresponding annotation such as group. + * + * @param string $name + * @return array + */ public function getAnnotationByName($name) { if (array_key_exists($name, $this->annotations)) { diff --git a/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php index 41462866b..894e9c252 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php @@ -10,6 +10,7 @@ class DirSetupUtil /** * Method used to clean export dir if needed and create new empty export dir. * + * @param string $fullPath * @return void */ public static function createGroupDir($fullPath) @@ -44,4 +45,4 @@ private static function rmdirRecursive($directory) rmdir($directory); } -} \ No newline at end of file +} diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index aa8c9e803..99f0bcdf7 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -20,7 +20,6 @@ class TestGenerator const REQUIRED_ENTITY_REFERENCE = 'createDataKey'; const GENERATED_DIR = '_generated'; - /** * Path to the export dir. * @@ -44,20 +43,26 @@ class TestGenerator /** * TestGenerator constructor. + * * @param string $exportDir + * @param array $cests */ private function __construct($exportDir, $cests) { // private constructor for factory $this->exportDirName = $exportDir ?? self::GENERATED_DIR; - $this->exportDirectory = rtrim(TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . - self::GENERATED_DIR . DIRECTORY_SEPARATOR . $exportDir, DIRECTORY_SEPARATOR); + $this->exportDirectory = rtrim( + TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . self::GENERATED_DIR . DIRECTORY_SEPARATOR . $exportDir, + DIRECTORY_SEPARATOR + ); $this->cests = $cests; } /** * Singleton method to retrieve Test Generator * + * @param string $dir + * @param array $cests * @return TestGenerator */ public static function getInstance($dir = null, $cests = null) From 539aadb016fe344937cb621f5ad5c95d11d1773d Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Thu, 12 Oct 2017 13:07:32 -0500 Subject: [PATCH 09/27] MQE-418: [Generator] Quote issue in generator for parameterArray (#16) * MQE-418: [Generator] Quote issue in generator for parameterArray - Removed all processing of parameterArray, as current implementation was causing the errors. - Simple setting fixes all known usecases for the parameterArray attribute. - Merged and Reverted erroneous merge. --- .../FunctionalTestingFramework/Util/TestGenerator.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 99f0bcdf7..75add59d1 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -367,15 +367,7 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) } if (isset($customActionAttributes['parameterArray'])) { - $paramsWithUniqueness = []; - $params = explode( - ',', - $this->stripWrappedQuotes(rtrim(ltrim($customActionAttributes['parameterArray'], '['), ']')) - ); - foreach ($params as $param) { - $paramsWithUniqueness[] = $this->addUniquenessFunctionCall($param); - } - $parameterArray = '[' . implode(',', $paramsWithUniqueness) . ']'; + $parameterArray = $customActionAttributes['parameterArray']; } if (isset($customActionAttributes['requiredAction'])) { From d0230bfde5770ee6ce2f0b967208ba7cbe026da0 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Thu, 12 Oct 2017 16:00:02 -0500 Subject: [PATCH 10/27] MQE-409: [Data Input] Action Groups need to extend persisted data as args - ActionGroupObject now correctly handles $datakey$ references. - Fixed in-line doc to match structure. - Code Review changes for comments. --- .../Test/Objects/ActionGroupObject.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index a88bcae61..19cb0838c 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -127,7 +127,18 @@ private function resolveNewAttribute($arguments, $attributeValue, $matches) $newAttributeVal = $attributeValue; foreach ($matches[1] as $var) { if (array_key_exists($var, $arguments)) { - $newAttributeVal = str_replace($var, $arguments[$var], $newAttributeVal); + if (preg_match('/\$\$[\w.\[\]\',]+\$\$/', $arguments[$var])) { + //if persisted $$data$$ was passed, return $$param.id$$ instead of {{$$param$$.id}} + $newAttributeVal = str_replace($var, trim($arguments[$var], '$'), $newAttributeVal); + $newAttributeVal = str_replace('{{', '$$', str_replace('}}', '$$', $newAttributeVal)); + } elseif (preg_match('/\$[\w.\[\]\',]+\$/', $arguments[$var])) { + //elseif persisted $data$ was passed, return $param.id$ instead of {{$param$.id}} + $newAttributeVal = str_replace($var, trim($arguments[$var], '$'), $newAttributeVal); + $newAttributeVal = str_replace('{{', '$', str_replace('}}', '$', $newAttributeVal)); + } else { + //else normal param replacement + $newAttributeVal = str_replace($var, $arguments[$var], $newAttributeVal); + } } } From 9c1f44e3d43760dec4de8764c6da5e7ee54207ba Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Fri, 13 Oct 2017 12:20:19 -0500 Subject: [PATCH 11/27] MQE-453:[DevOps] Add optional arg for consolidated test run - add param for config flag and env to TestManifest Constructor - change generate tests signature for new flags --- .../Util/TestGenerator.php | 26 +++++--- .../Util/TestManifest.php | 66 ++++++++++++++++++- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index 75add59d1..ad6074286 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -120,16 +120,25 @@ private function createCestFile($cestPhp, $filename) * Assemble ALL PHP strings using the assembleAllCestPhp function. Loop over and pass each array item * to the createCestFile function. * + * @param string $runConfig + * @param string $env * @return void */ - public function createAllCestFiles() + public function createAllCestFiles($runConfig = null, $env = null) { DirSetupUtil::createGroupDir($this->exportDirectory); - $cestPhpArray = $this->assembleAllCestPhp(); + + // create our manifest file here + $testManifest = new TestManifest($this->exportDirectory, $runConfig, $env); + $cestPhpArray = $this->assembleAllCestPhp($testManifest); foreach ($cestPhpArray as $cestPhpFile) { $this->createCestFile($cestPhpFile[1], $cestPhpFile[0]); } + + if ($testManifest->getManifestConfig() === TestManifest::SINGLE_RUN_CONFIG) { + $testManifest->recordPathToExportDir(); + } } /** @@ -137,6 +146,7 @@ public function createAllCestFiles() * Create all of the PHP strings for a Test. Concatenate the strings together. * * @param \Magento\FunctionalTestingFramework\Test\Objects\CestObject $cestObject + * @throws TestReferenceException * @return string */ private function assembleCestPhp($cestObject) @@ -168,24 +178,24 @@ private function assembleCestPhp($cestObject) /** * Load ALL Cest objects. Loop over and pass each to the assembleCestPhp function. * + * @param TestManifest $testManifest * @return array */ - private function assembleAllCestPhp() + private function assembleAllCestPhp($testManifest) { $cestObjects = $this->loadAllCestObjects(); $cestPhpArray = []; - // create our manifest file here - $testManifest = new TestManifest($this->exportDirectory); - foreach ($cestObjects as $cest) { $name = $cest->getName(); $name = $string = str_replace(' ', '', $name); $php = $this->assembleCestPhp($cest); $cestPhpArray[] = [$name, $php]; - //write to manifest here - $testManifest->recordCest($cest->getName(), $cest->getTests()); + //write to manifest here if config is not single run + if ($testManifest->getManifestConfig() != TestManifest::SINGLE_RUN_CONFIG) { + $testManifest->recordCest($cest->getName(), $cest->getTests()); + } } return $cestPhpArray; diff --git a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php index 2545141c8..02146b664 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php @@ -10,6 +10,9 @@ class TestManifest { + const SINGLE_RUN_CONFIG = 'singleRun'; + const DEFAULT_BROWSER = 'chrome'; + /** * Test Manifest file path. * @@ -17,6 +20,20 @@ class TestManifest */ private $filePath; + /** + * Test Manifest environment flag. This is added to each dir or file in order for tests to execute properly. + * + * @var string $environment + */ + private $environment = self::DEFAULT_BROWSER; + + /** + * Type of manifest to generate. (Currently describes whether to path to a dir or for each test). + * + * @var string + */ + private $runTypeConfig; + /** * Relative dir path from functional yml file. For devOps execution flexibility. * @@ -28,14 +45,32 @@ class TestManifest * TestManifest constructor. * * @param string $path + * @param string $runConfig + * @param string $env */ - public function __construct($path) + public function __construct($path, $runConfig, $env) { $this->relativeDirPath = substr($path, strlen(dirname(dirname(TESTS_BP))) + 1); $filePath = $path . DIRECTORY_SEPARATOR . 'testManifest.txt'; $this->filePath = $filePath; $fileResource = fopen($filePath, 'w'); fclose($fileResource); + + $this->runTypeConfig = $runConfig; + + if ($env) { + $this->environment = $env; + } + } + + /** + * Returns a string indicating the generation config (e.g. singleRun). + * + * @return string + */ + public function getManifestConfig() + { + return $this->runTypeConfig; } /** @@ -51,9 +86,36 @@ public function recordCest($cestName, $tests) foreach ($tests as $test) { $line = $this->relativeDirPath . DIRECTORY_SEPARATOR . $cestName . '.php:' . $test->getName(); - fwrite($fileResource, $line ."\n"); + fwrite($fileResource, $this->appendDefaultBrowser($line) ."\n"); } fclose($fileResource); } + + /** + * Function which simple prints the export dir as part of the manifest file rather than an itemized list of + * cestFile:testname. + * + * @return void + */ + public function recordPathToExportDir() + { + $fileResource = fopen($this->filePath, 'a'); + + $line = $this->relativeDirPath . DIRECTORY_SEPARATOR; + fwrite($fileResource, $this->appendDefaultBrowser($line) ."\n"); + + fclose($fileResource); + } + + /** + * Function which appends the --env flag to the test. This is needed to properly execute all tests in codeception. + * + * @param string $line + * @return string + */ + private function appendDefaultBrowser($line) + { + return "${line} --env " . $this->environment; + } } From c16ad40176eaaaadfe72270d90e0b7bd8634974e Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Mon, 16 Oct 2017 09:47:30 -0500 Subject: [PATCH 12/27] MQE-460:OperationDataArrayResolver does not correctly resolve meta data object type's key attribute. - fixed array reference to key instead of type when building request data --- .../DataGenerator/Persist/OperationDataArrayResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php index 49cb535a8..93ab204bc 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php @@ -73,7 +73,7 @@ public function resolveOperationDataArray($entityObject, $operationMetadata, $op foreach ($operationMetadata as $operationElement) { if ($operationElement->getType() == OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME) { $entityObj = $this->resolveOperationObjectAndEntityData($entityObject, $operationElement->getValue()); - $operationDataArray[$operationElement->getValue()] = + $operationDataArray[$operationElement->getKey()] = $this->resolveOperationDataArray($entityObj, $operationElement->getNestedMetadata(), $operation); continue; } From 6440539a3df2d736df9d2e88e0159359ea0b08ee Mon Sep 17 00:00:00 2001 From: Tom Reece Date: Mon, 16 Oct 2017 09:58:58 -0500 Subject: [PATCH 13/27] MQE-431: Automatically check for copyright at the top of every framework file - Add copyright to more files that were recently added --- .../Config/FileResolver/Root.php | 3 ++- .../FunctionalTestingFramework/Suite/etc/sampleSuite.xml | 9 ++++++++- .../FunctionalTestingFramework/Suite/etc/suiteSchema.xsd | 9 ++++++++- .../Util/Filesystem/DirSetupUtil.php | 4 ++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php index abb59e6c1..e728fa418 100644 --- a/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php +++ b/src/Magento/FunctionalTestingFramework/Config/FileResolver/Root.php @@ -1,8 +1,9 @@ + + @@ -13,4 +20,4 @@ - \ No newline at end of file + diff --git a/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd b/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd index 6984c9b45..444228586 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd @@ -1,4 +1,11 @@ + + @@ -53,4 +60,4 @@ - \ No newline at end of file + diff --git a/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php index 894e9c252..405b2a4f7 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php +++ b/src/Magento/FunctionalTestingFramework/Util/Filesystem/DirSetupUtil.php @@ -1,4 +1,8 @@ Date: Mon, 16 Oct 2017 11:05:20 -0500 Subject: [PATCH 14/27] MQE-431: Automatically check for copyright at the top of every framework file - Refactor, rename, add comment to blacklist file --- bin/blacklist.txt | 9 +++++++++ bin/copyright-check | 30 +++++++++++++++--------------- bin/copyright-ignore.txt | 1 - 3 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 bin/blacklist.txt delete mode 100644 bin/copyright-ignore.txt diff --git a/bin/blacklist.txt b/bin/blacklist.txt new file mode 100644 index 000000000..c3b30ac45 --- /dev/null +++ b/bin/blacklist.txt @@ -0,0 +1,9 @@ +# Specify directories or files for the copyright-check to ignore. +# +# e.g. +# path/to/directory +# path/to/file.php +# +# THIS FILE CANNOT CONTAIN BLANK LINES +# +dev/tests/static/Magento/Sniffs/Annotations \ No newline at end of file diff --git a/bin/copyright-check b/bin/copyright-check index 3c1b28c34..c2552b7cc 100755 --- a/bin/copyright-check +++ b/bin/copyright-check @@ -1,26 +1,26 @@ #!/bin/bash -IGNORE_PATH='bin/copyright-ignore.txt' -FILE_EXTENSIONS_TO_CHECK='.php\|.xml\|.xsd' - +FILE_EXTENSIONS='.php\|.xml\|.xsd' +BLACKLIST='bin/blacklist.txt' RESULT='' -# Iterate through all php, xml, and xsd files that are part of the Git repo -# (excluding any matching lines from the ignore file) -for i in `git ls-tree --full-tree -r --name-only HEAD | grep $FILE_EXTENSIONS_TO_CHECK | grep -v -f $IGNORE_PATH` +# Iterate through the list of tracked files +# that have the expected extensions +# that are not ignored +for i in `git ls-tree --full-tree -r --name-only HEAD | grep $FILE_EXTENSIONS | grep -v -f $BLACKLIST` do - if echo `cat $i` | grep -q -v "Copyright © Magento, Inc. All rights reserved."; then - # Copyright is missing - RESULT+="$i\n" - fi + if echo `cat $i` | grep -q -v "Copyright © Magento, Inc. All rights reserved."; then + # Copyright is missing + RESULT+="$i\n" + fi done if [[ ! -z $RESULT ]]; then - printf "\nTHE FOLLOWING FILES ARE MISSING THE MAGENTO COPYRIGHT:\n\n" - printf " Copyright © Magento, Inc. All rights reserved.\n" - printf " See COPYING.txt for license details.\n\n" - printf "$RESULT\n" - exit 1 + printf "\nTHE FOLLOWING FILES ARE MISSING THE MAGENTO COPYRIGHT:\n\n" + printf " Copyright © Magento, Inc. All rights reserved.\n" + printf " See COPYING.txt for license details.\n\n" + printf "$RESULT\n" + exit 1 fi # Success! diff --git a/bin/copyright-ignore.txt b/bin/copyright-ignore.txt deleted file mode 100644 index 02bc262b2..000000000 --- a/bin/copyright-ignore.txt +++ /dev/null @@ -1 +0,0 @@ -dev/tests/static/Magento/Sniffs/Annotations/ From 94987417311cc2aa9680d2014b4a37c954696e60 Mon Sep 17 00:00:00 2001 From: Tom Reece Date: Mon, 16 Oct 2017 11:16:06 -0500 Subject: [PATCH 15/27] MQE-431: Automatically check for copyright at the top of every framework file - Add copyright to copyright-check --- bin/blacklist.txt | 18 +++++++++--------- bin/copyright-check | 4 ++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/bin/blacklist.txt b/bin/blacklist.txt index c3b30ac45..c85d25b5c 100644 --- a/bin/blacklist.txt +++ b/bin/blacklist.txt @@ -1,9 +1,9 @@ -# Specify directories or files for the copyright-check to ignore. -# -# e.g. -# path/to/directory -# path/to/file.php -# -# THIS FILE CANNOT CONTAIN BLANK LINES -# -dev/tests/static/Magento/Sniffs/Annotations \ No newline at end of file +################################################################### +# Specify directories or files for the copyright-check to ignore. # +# # +# THIS FILE CANNOT CONTAIN BLANK LINES # +################################################################### +bin/blacklist.txt +dev/tests/static/Magento/Sniffs/Annotations/Helper.php +dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedAttributesSniff.php +dev/tests/static/Magento/Sniffs/Annotations/RequireAnnotatedMethodsSniff.php \ No newline at end of file diff --git a/bin/copyright-check b/bin/copyright-check index c2552b7cc..79fe8df06 100755 --- a/bin/copyright-check +++ b/bin/copyright-check @@ -1,5 +1,9 @@ #!/bin/bash +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + + FILE_EXTENSIONS='.php\|.xml\|.xsd' BLACKLIST='bin/blacklist.txt' RESULT='' From 2da5129492e80cfd76a5381c36eb394ac7d7c84a Mon Sep 17 00:00:00 2001 From: Ian Meron Date: Mon, 16 Oct 2017 13:33:50 -0500 Subject: [PATCH 16/27] MQE-425:Create test to validate changes between commits with php unit - add verification and unit test structure - add verification and unit tests - add test utils - add bootstrap setup file and phpunit.xml configuration - change object handler classes to trigger notices for missing xml --- dev/tests/_bootstrap.php | 63 +++++++ dev/tests/phpunit.xml | 23 +++ .../Test/Util/ActionMergeUtilTest.php | 159 ++++++++++++++++++ .../Util/DataObjectHandlerReflectionUtil.php | 47 ++++++ .../ObjectHandlerReflectionUtilInterface.php | 24 +++ .../Resources/BasicFunctionalCest.txt | 128 ++++++++++++++ .../TestModule/ActionObject/placeholder.txt | 0 .../TestModule/Cest/basicFunctionalCest.xml | 114 +++++++++++++ .../TestModule/Data/placeholder.txt | 0 .../TestModule/Metadata/placeholder.txt | 0 .../TestModule/Page/placeholder.txt | 0 .../TestModule/Section/placeholder.txt | 0 .../Tests/BasicCestGenerationTest.php | 42 +++++ dev/tests/verification/Util/FileDiffUtil.php | 53 ++++++ .../Handlers/DataObjectHandler.php | 38 +++-- .../Page/Handlers/PageObjectHandler.php | 9 +- .../Page/Handlers/SectionObjectHandler.php | 9 +- .../Test/Handlers/CestObjectHandler.php | 5 + 18 files changed, 697 insertions(+), 17 deletions(-) create mode 100644 dev/tests/_bootstrap.php create mode 100644 dev/tests/phpunit.xml create mode 100644 dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php create mode 100644 dev/tests/unit/Util/DataObjectHandlerReflectionUtil.php create mode 100644 dev/tests/unit/Util/ObjectHandlerReflectionUtilInterface.php create mode 100644 dev/tests/verification/Resources/BasicFunctionalCest.txt create mode 100644 dev/tests/verification/TestModule/ActionObject/placeholder.txt create mode 100644 dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml create mode 100644 dev/tests/verification/TestModule/Data/placeholder.txt create mode 100644 dev/tests/verification/TestModule/Metadata/placeholder.txt create mode 100644 dev/tests/verification/TestModule/Page/placeholder.txt create mode 100644 dev/tests/verification/TestModule/Section/placeholder.txt create mode 100644 dev/tests/verification/Tests/BasicCestGenerationTest.php create mode 100644 dev/tests/verification/Util/FileDiffUtil.php diff --git a/dev/tests/_bootstrap.php b/dev/tests/_bootstrap.php new file mode 100644 index 000000000..57721a44e --- /dev/null +++ b/dev/tests/_bootstrap.php @@ -0,0 +1,63 @@ + 'http://baseurl:8080', + 'MAGENTO_BACKEND_NAME' => 'admin', + 'MAGENTO_ADMIN_USERNAME' => 'admin', + 'MAGENTO_ADMIN_PASSWORD' => 'admin123' +]; + +foreach ($TEST_ENVS as $key => $value) { + $_ENV[$key] = $value; +} + +// Add our test module to the whitelist +putenv('MODULE_WHITELIST=Magento_TestModule'); + + +// Define our own set of paths for the tests +defined('FW_BP') || define('FW_BP', PROJECT_ROOT); + +$RELATIVE_TESTS_MODULE_PATH = DIRECTORY_SEPARATOR . 'verification'; + +defined('TESTS_BP') || define('TESTS_BP', __DIR__); +defined('TESTS_MODULE_PATH') || define('TESTS_MODULE_PATH', TESTS_BP . $RELATIVE_TESTS_MODULE_PATH); + +$utilDir = DIRECTORY_SEPARATOR . 'Util'. DIRECTORY_SEPARATOR . '*.php'; + +//Load required util files from functional dir +$functionalUtilFiles = glob(TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . $utilDir); +foreach (sortInterfaces($functionalUtilFiles) as $functionalUtilFile) { + require($functionalUtilFile); +} + +//Load required util files from unit dir +$unitUtilFiles = glob(TESTS_BP . DIRECTORY_SEPARATOR . 'unit' . $utilDir); +foreach (sortInterfaces($unitUtilFiles) as $unitUtilFile) { + require($unitUtilFile); +} + +function sortInterfaces($files) +{ + $bottom = []; + $top = []; + foreach ($files as $file) { + if (strstr(strtolower($file), 'interface')) { + $top[] = $file; + continue; + } + + $bottom[] = $file; + } + + return array_merge($top, $bottom); +} diff --git a/dev/tests/phpunit.xml b/dev/tests/phpunit.xml new file mode 100644 index 000000000..e06631268 --- /dev/null +++ b/dev/tests/phpunit.xml @@ -0,0 +1,23 @@ + + + + verification + + + unit + + + + + ../../src/Magento/FunctionalTestingFramework/DataGenerator + ../../src/Magento/FunctionalTestingFramework/Page + ../../src/Magento/FunctionalTestingFramework/Suite + ../../src/Magento/FunctionalTestingFramework/Test + ../../src/Magento/FunctionalTestingFramework/Util + + + \ No newline at end of file diff --git a/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php new file mode 100644 index 000000000..1c5d68724 --- /dev/null +++ b/dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionMergeUtilTest.php @@ -0,0 +1,159 @@ +resolveActionSteps($actions); + $orderedActionKeys = array_keys($orderedActions); + + $this->assertEquals($testObjNamePosBeforeFirst, $orderedActionKeys[0]); + $this->assertEquals($testObjNamePosFirst, $orderedActionKeys[1]); + $this->assertEquals($testObjNamePosEnd, $orderedActionKeys[$actionsLength + 1]); + $this->assertEquals($testObjNamePosAfterEnd, $orderedActionKeys[$actionsLength + 2]); + } + + /** + * Test to validate action steps properly resolve section element references. + * + * @return void + */ + public function testResolveActionStepSectionData() + { + //TODO implement section object mocker and test + } + + /** + * Test to validate action steps properly resolve page references. + * + * @return void + */ + public function resolveActionStepPageData() + { + //TODO implement page object mocker and test + } + + /** + * Test to validate action steps properly resolve entity data references. + * + * @return void + */ + public function testResolveActionStepEntityData() + { + $dataObjectName = 'myObject'; + $dataObjectType = 'testObject'; + $dataFieldName = 'myfield'; + $dataFieldValue = 'myValue'; + $userInputKey = "userInput"; + $userinputValue = "{{" . "${dataObjectName}.${dataFieldName}}}"; + $actionName = "myAction"; + $actionType = "myCustomType"; + + + // Set up mock data object + $mockData = [$dataFieldName => $dataFieldValue]; + $mockDataObject = new EntityDataObject($dataObjectName, $dataObjectType, $mockData, null, null, null); + + // Set up mock DataObject Handler + $mockDataHandler = $this->createMock(DataObjectHandler::class); + $mockDataHandler->expects($this->any()) + ->method('getObject') + ->with($this->matches($dataObjectName)) + ->willReturn($mockDataObject); + DataObjectHandlerReflectionUtil::setupMock($mockDataHandler); + + // Create test object and action object + $actionAttributes = [$userInputKey => $userinputValue]; + $actions[$actionName] = new ActionObject($actionName, $actionType, $actionAttributes); + + $this->assertEquals($userinputValue, $actions[$actionName]->getCustomActionAttributes()[$userInputKey]); + + $mergeUtil = new ActionMergeUtil(); + $resolvedActions = $mergeUtil->resolveActionSteps($actions); + + $this->assertEquals($dataFieldValue, $resolvedActions[$actionName]->getCustomActionAttributes()[$userInputKey]); + + DataObjectHandlerReflectionUtil::tearDown(); + } +} diff --git a/dev/tests/unit/Util/DataObjectHandlerReflectionUtil.php b/dev/tests/unit/Util/DataObjectHandlerReflectionUtil.php new file mode 100644 index 000000000..557ab5772 --- /dev/null +++ b/dev/tests/unit/Util/DataObjectHandlerReflectionUtil.php @@ -0,0 +1,47 @@ +bindTo(null, DataObjectHandler::class); + $setMockStatic($mockObject); + } + + /** + * Sets the Data Object Handler Instance to a null value for re-initialization. + */ + public static function tearDown() + { + $resetStatic = function () { + static::$DATA_OBJECT_HANDLER = null; + }; + + $resetMockStatic = $resetStatic->bindTo(null, DataObjectHandler::class); + $resetMockStatic(); + } +} \ No newline at end of file diff --git a/dev/tests/unit/Util/ObjectHandlerReflectionUtilInterface.php b/dev/tests/unit/Util/ObjectHandlerReflectionUtilInterface.php new file mode 100644 index 000000000..145c1a0e3 --- /dev/null +++ b/dev/tests/unit/Util/ObjectHandlerReflectionUtilInterface.php @@ -0,0 +1,24 @@ +amOnPage("/beforeUrl"); + } + + public function _after(AcceptanceTester $I) + { + $I->amOnPage("/afterUrl"); + } + + /** + * @Severity(level = SeverityLevel::SEVERE) + * @Title("Basic Functional Test") + * @Features({"Hardcoded Functional Test"}) + * @Stories({"MQE-425"}) + * @Parameter(name = "AcceptanceTester", value="$I") + * @group functionalTest + * @param AcceptanceTester $I + * @return void + */ + public function BasicFunctionalTest(AcceptanceTester $I) + { + $someVar = $I->grabValueFrom(); + $I->acceptPopup(); + $I->amOnPage("/test/url"); + $I->appendField(".functionalTestSelector", $someVar); + $I->attachFile(".functionalTestSelector", "testFileAttachment"); + $I->cancelPopup(); + $I->checkOption(".functionalTestSelector"); + $I->click(".functionalTestSelector"); + $I->clickWithLeftButton(".functionalTestSelector"); + $I->clickWithRightButton(".functionalTestSelector"); + $I->closeTab(); + $I->conditionalClick(".functionalTestSelector", ".functionalDependentTestSelector"); + $I->dontSee("someInput", ".functionalTestSelector"); + $I->dontSeeCheckboxIsChecked(".functionalTestSelector"); + $I->dontSeeCookie("someInput"); + $I->dontSeeCurrentUrlEquals("/functionalUrl"); + $I->dontSeeCurrentUrlMatches("/functionalUrl"); + $I->dontSeeElement(".functionalTestSelector"); + $I->dontSeeElementInDOM(".functionalTestSelector"); + $I->dontSeeInCurrentUrl("/functionalUrl"); + $I->dontSeeInField(".functionalTestSelector", $someVar); + $I->dontSeeInPageSource("someInput"); + $I->dontSeeInSource(""); + $I->dontSeeInTitle("someInput"); + $I->dontSeeLink("someInput", "/functionalUrl"); + $I->dontSeeOptionIsSelected(".functionalTestSelector", "someInput"); + $I->doubleClick(".functionalTestSelector"); + $I->dragAndDrop(".functionalTestSelector", ".functionalTestSelector2"); + $I->executeJS("someJSFunction"); + $I->fillField(".functionalTestSelector", "someInput"); + $grabAttributeVar = $I->grabAttributeFrom(".functionalTestSelector", "someInput"); + $grabCookieVar = $I->grabCookie("grabCookieInput", ['domain' => 'www.google.com']); + $grabUrlVar = $I->grabFromCurrentUrl("/grabCurrentUrl"); + $grabMultipleVar = $I->grabMultiple(".functionalTestSelector"); + $I->grabTextFrom(".functionalTestSelector"); + $grabValueVar = $I->grabValueFrom(".functionalTestSelector"); + $I->makeScreenshot("screenShotInput"); + $I->maximizeWindow(); + $I->moveBack(); + $I->moveForward(); + $I->moveMouseOver(".functionalTestSelector"); + $I->openNewTab(); + $I->pauseExecution(); + $I->pressKey(".functionalTestSelector"); + $I->reloadPage(); + $I->resetCookie("cookieInput"); + $I->resizeWindow(0, 0); + $I->scrollTo(".functionalTestSelector"); + $I->see("someInput", ".functionalTestSelector"); + $I->seeCheckboxIsChecked(".functionalTestSelector"); + $I->seeCookie("someInput"); + $I->seeCurrentUrlEquals("/functionalUrl"); + $I->seeCurrentUrlMatches("/functionalUrl"); + $I->seeElement(".functionalTestSelector"); + $I->seeElementInDOM(".functionalTestSelector"); + $I->seeInCurrentUrl("/functionalUrl"); + $I->seeInField(".functionalTestSelector", "someInput"); + $I->seeInPageSource(""); + $I->seeInPopup("someInput"); + $I->seeInSource(""); + $I->seeInTitle("someInput"); + $I->seeLink("someInput", "/functionalUrl"); + $I->seeNumberOfElements(".functionalTestSelector", $someVar); + $I->seeOptionIsSelected(".functionalTestSelector", "someInput"); + $I->selectOption(".functionalTestSelector"); + $I->setCookie("someInput", "someCookieValue"); + $I->switchToIFrame("someInput"); + $I->switchToNextTab(); + $I->switchToPreviousTab(); + $I->switchToWindow(); + $I->typeInPopup("someInput"); + $I->uncheckOption(".functionalTestSelector"); + $I->unselectOption(".functionalTestSelector", "someInput"); + $I->wait(30); + $I->waitForElement(".functionalTestSelector", 30); + $I->waitForElementNotVisible(".functionalTestSelector", 30); + $I->waitForElementVisible(".functionalTestSelector", 30); + $I->waitForJS("someJsFunction", 30); + $I->waitForText("someInput", 30, ".functionalTestSelector"); + } +} diff --git a/dev/tests/verification/TestModule/ActionObject/placeholder.txt b/dev/tests/verification/TestModule/ActionObject/placeholder.txt new file mode 100644 index 000000000..e69de29bb diff --git a/dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml b/dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml new file mode 100644 index 000000000..6d2fa6919 --- /dev/null +++ b/dev/tests/verification/TestModule/Cest/basicFunctionalCest.xml @@ -0,0 +1,114 @@ + + + + + + + + + <group value="functional"/> + <features value="Basic Functional Cest"/> + <stories value="MQE-305"/> + </annotations> + <before> + <amOnPage url="/beforeUrl" mergeKey="beforeAmOnPageKey"/> + </before> + <after> + <amOnPage url="/afterUrl" mergeKey="afterAmOnPageKey"/> + </after> + <test name="BasicFunctionalTest"> + <annotations> + <severity value="SEVERE"/> + <title value="Basic Functional Test"/> + <group value="functionalTest"/> + <features value="Hardcoded Functional Test"/> + <stories value="MQE-425"/> + </annotations> + <grabValueFrom returnVariable="someVar" mergeKey="someVarDefinition"/> + <acceptPopup mergeKey="acceptPopupKey1"/> + <amOnPage mergeKey="amOnPageKey1" url="/test/url"/> + <appendField variable="someVar" selector=".functionalTestSelector" userInput="someInput" mergeKey="appendFieldKey1" /> + <attachFile userInput="testFileAttachment" selector=".functionalTestSelector" mergeKey="attachFileKey1" /> + <cancelPopup mergeKey="cancelPopupKey1"/> + <checkOption selector=".functionalTestSelector" mergeKey="checkOptionKey1"/> + <click selector=".functionalTestSelector" mergeKey="clickKey1"/> + <clickWithLeftButton selector=".functionalTestSelector" mergeKey="clickWithLeftButtonKey1"/> + <clickWithRightButton selector=".functionalTestSelector" mergeKey="clickWithRightButtonKey1"/> + <closeTab mergeKey="closeTabKey1"/> + <conditionalClick selector=".functionalTestSelector" dependentSelector=".functionalDependentTestSelector" mergeKey="conditionalClickKey1"/> + <dontSee userInput="someInput" selector=".functionalTestSelector" mergeKey="dontSeeKey1" /> + <dontSeeCheckboxIsChecked selector=".functionalTestSelector" mergeKey="dontSeeCheckboxIsCheckedKey1"/> + <dontSeeCookie userInput="someInput" mergeKey="dontSeeCookieKey1"/> + <dontSeeCurrentUrlEquals url="/functionalUrl" mergeKey="dontSeeCurrentUrlEqualsKey1"/> + <dontSeeCurrentUrlMatches url="/functionalUrl" mergeKey="dontSeeCurrentUrlMatchesKey1"/> + <dontSeeElement selector=".functionalTestSelector" mergeKey="dontSeeElementKey1"/> + <dontSeeElementInDOM selector=".functionalTestSelector" mergeKey="dontSeeElementInDOMKey1"/> + <dontSeeInCurrentUrl url="/functionalUrl" mergeKey="dontSeeInCurrentUrlKey1"/> + <dontSeeInField variable="someVar" selector=".functionalTestSelector" userInput="someInput" mergeKey="dontSeeInFieldKey1" /> + <dontSeeInPageSource userInput="someInput" mergeKey="dontSeeInPageSourceKey1"/> + <dontSeeInSource html=""<myHtmlHere>"" mergeKey="dontSeeInSourceKey1"/> + <dontSeeInTitle userInput="someInput" mergeKey="dontSeeInTitleKey1"/> + <dontSeeLink userInput="someInput" url="/functionalUrl" mergeKey="dontSeeLinkKey1" /> + <dontSeeOptionIsSelected selector=".functionalTestSelector" userInput="someInput" mergeKey="dontSeeOptionIsSelectedKey1" /> + <doubleClick selector=".functionalTestSelector" mergeKey="doubleClickKey1"/> + <dragAndDrop selector1=".functionalTestSelector" selector2=".functionalTestSelector2" mergeKey="dragAndDropKey1" /> + <executeJS function="someJSFunction" mergeKey="executeJSKey1"/> + <fillField selector=".functionalTestSelector" userInput="someInput" mergeKey="fillFieldKey1" /> + <grabAttributeFrom returnVariable="grabAttributeVar" selector=".functionalTestSelector" userInput="someInput" mergeKey="grabAttributeFromKey1" /> + <grabCookie returnVariable="grabCookieVar" userInput="grabCookieInput" parameterArray="['domain' => 'www.google.com']" mergeKey="grabCookieKey1" /> + <grabFromCurrentUrl returnVariable="grabUrlVar" url="/grabCurrentUrl" mergeKey="grabFromCurrentUrlKey1" /> + <grabMultiple selector=".functionalTestSelector" returnVariable="grabMultipleVar" mergeKey="grabMultipleKey1" /> + <grabTextFrom returnVariable="" selector=".functionalTestSelector" mergeKey="grabTextFromKey1" /> + <grabValueFrom selector=".functionalTestSelector" returnVariable="grabValueVar" mergeKey="grabValueFromKey1" /> + <makeScreenshot userInput="screenShotInput" mergeKey="makeScreenshotKey1"/> + <maximizeWindow mergeKey="maximizeWindowKey1"/> + <moveBack mergeKey="moveBackKey1"/> + <moveForward mergeKey="moveForwardKey1"/> + <moveMouseOver selector=".functionalTestSelector" mergeKey="moveMouseOverKey1"/> + <openNewTab mergeKey="openNewTabKey1"/> + <pauseExecution mergeKey="pauseExecutionKey1"/> + <pressKey selector=".functionalTestSelector" mergeKey="pressKeyKey1"/> + <reloadPage mergeKey="reloadPageKey1"/> + <resetCookie userInput="cookieInput" mergeKey="resetCookieKey1"/> + <resizeWindow width="0" height="0" mergeKey="resizeWindowKey1" /> + <scrollTo selector=".functionalTestSelector" mergeKey="scrollToKey1"/> + <see userInput="someInput" selector=".functionalTestSelector" mergeKey="seeKey1" /> + <seeCheckboxIsChecked selector=".functionalTestSelector" mergeKey="seeCheckboxIsCheckedKey1"/> + <seeCookie userInput="someInput" mergeKey="seeCookieKey1"/> + <seeCurrentUrlEquals url="/functionalUrl" mergeKey="seeCurrentUrlEqualsKey1"/> + <seeCurrentUrlMatches url="/functionalUrl" mergeKey="seeCurrentUrlMatchesKey1"/> + <seeElement selector=".functionalTestSelector" mergeKey="seeElementKey1"/> + <seeElementInDOM selector=".functionalTestSelector" mergeKey="seeElementInDOMKey1"/> + <seeInCurrentUrl url="/functionalUrl" mergeKey="seeInCurrentUrlKey1"/> + <seeInField selector=".functionalTestSelector" userInput="someInput" mergeKey="seeInFieldKey1" /> + <seeInPageSource html=""<myHtmlHere>"" mergeKey="seeInPageSourceKey1"/> + <seeInPopup userInput="someInput" mergeKey="seeInPopupKey1"/> + <seeInSource html=""<myHtmlHere>"" mergeKey="seeInSourceKey1"/> + <seeInTitle userInput="someInput" mergeKey="seeInTitleKey1"/> + <seeLink userInput="someInput" url="/functionalUrl" mergeKey="seeLinkKey1" /> + <seeNumberOfElements variable="someVar" selector=".functionalTestSelector" mergeKey="seeNumberOfElementsKey1"/> + <seeOptionIsSelected selector=".functionalTestSelector" userInput="someInput" mergeKey="seeOptionIsSelectedKey1" /> + <selectOption selector=".functionalTestSelector" mergeKey="selectOptionKey1"/> + <setCookie value="someCookieValue" userInput="someInput" mergeKey="setCookieKey1" /> + <switchToIFrame userInput="someInput" mergeKey="switchToIFrameKey1"/> + <switchToNextTab mergeKey="switchToNextTabKey1"/> + <switchToPreviousTab mergeKey="switchToPreviousTabKey1"/> + <switchToWindow mergeKey="switchToWindowKey1"/> + <typeInPopup userInput="someInput" mergeKey="typeInPopupKey1"/> + <uncheckOption selector=".functionalTestSelector" mergeKey="uncheckOptionKey1"/> + <unselectOption selector=".functionalTestSelector" userInput="someInput" mergeKey="unselectOptionKey1" /> + <wait time="30" mergeKey="waitKey1"/> + <waitForElement time="30" selector=".functionalTestSelector" mergeKey="waitForElementKey1" /> + <waitForElementNotVisible selector=".functionalTestSelector" time="30" mergeKey="waitForElementNotVisibleKey1" /> + <waitForElementVisible selector=".functionalTestSelector" time="30" mergeKey="waitForElementVisibleKey1" /> + <waitForJS function="someJsFunction" time="30" mergeKey="waitForJSKey1" /> + <waitForText selector=".functionalTestSelector" userInput="someInput" time="30" mergeKey=""/> + </test> + </cest> +</config> \ No newline at end of file diff --git a/dev/tests/verification/TestModule/Data/placeholder.txt b/dev/tests/verification/TestModule/Data/placeholder.txt new file mode 100644 index 000000000..e69de29bb diff --git a/dev/tests/verification/TestModule/Metadata/placeholder.txt b/dev/tests/verification/TestModule/Metadata/placeholder.txt new file mode 100644 index 000000000..e69de29bb diff --git a/dev/tests/verification/TestModule/Page/placeholder.txt b/dev/tests/verification/TestModule/Page/placeholder.txt new file mode 100644 index 000000000..e69de29bb diff --git a/dev/tests/verification/TestModule/Section/placeholder.txt b/dev/tests/verification/TestModule/Section/placeholder.txt new file mode 100644 index 000000000..e69de29bb diff --git a/dev/tests/verification/Tests/BasicCestGenerationTest.php b/dev/tests/verification/Tests/BasicCestGenerationTest.php new file mode 100644 index 000000000..d5644e0d4 --- /dev/null +++ b/dev/tests/verification/Tests/BasicCestGenerationTest.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\verification\Tests; + +use Magento\FunctionalTestingFramework\Test\Handlers\CestObjectHandler; +use Magento\FunctionalTestingFramework\Util\TestGenerator; +use PHPUnit\Framework\TestCase; +use tests\verification\Util\FileDiffUtil; + +class BasicCestGenerationTest extends TestCase +{ + const BASIC_FUNCTIONAL_CEST = 'BasicFunctionalCest'; + const RESOURCES_PATH = __DIR__ . '/../Resources'; + + /** + * Tests flat generation of a hardcoded cest file with no external references. + */ + public function testBasicGeneration() + { + $cest = CestObjectHandler::getInstance()->getObject(self::BASIC_FUNCTIONAL_CEST); + $test = TestGenerator::getInstance(null, [$cest]); + $test->createAllCestFiles(); + + $cestFile = $test->getExportDir() . + DIRECTORY_SEPARATOR . + self::BASIC_FUNCTIONAL_CEST . + ".php"; + + $this->assertTrue(file_exists($cestFile)); + + $fileDiffUtil = new FileDiffUtil( + self::RESOURCES_PATH . DIRECTORY_SEPARATOR . self::BASIC_FUNCTIONAL_CEST . ".txt", + $cestFile + ); + + $diffResult = $fileDiffUtil->diffContents(); + $this->assertNull($diffResult, $diffResult); + } +} \ No newline at end of file diff --git a/dev/tests/verification/Util/FileDiffUtil.php b/dev/tests/verification/Util/FileDiffUtil.php new file mode 100644 index 000000000..a9772eab5 --- /dev/null +++ b/dev/tests/verification/Util/FileDiffUtil.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace tests\verification\Util; + +class FileDiffUtil +{ + /** + * Object which represents the expected file + * + * @var array|bool + */ + private $expectedFile; + + + /** + * Object which represents the actual file + * + * @var array|bool + */ + private $actualFile; + + /** + * FileDiffUtil constructor. + * + * @param string $expectedFilePath + * @param string $actualFilePath + */ + public function __construct($expectedFilePath, $actualFilePath) + { + $this->expectedFile = file($expectedFilePath); + $this->actualFile = file($actualFilePath); + } + + /** + * Function which does a line by line comparison between the contents of the two files fed to the constructor. + * + * @return null|string + */ + public function diffContents() + { + $differingContent = null; + foreach ($this->actualFile as $line_num => $line) { + if ($line != $this->expectedFile[$line_num]) { + return $this->expectedFile[$line_num] . "was expected, but found: ${line} on line ${line_num}."; + } + } + + return $differingContent; + } +} \ No newline at end of file diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php index f52659b34..8f1089ff6 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/DataObjectHandler.php @@ -62,14 +62,8 @@ class DataObjectHandler implements ObjectHandlerInterface public static function getInstance() { if (!self::$DATA_OBJECT_HANDLER) { - $entityParser = ObjectManagerFactory::getObjectManager()->create(DataProfileSchemaParser::class); - $entityParsedData = $entityParser->readDataProfiles(); - - if (!$entityParsedData) { - throw new \Exception("No entities could be parsed from xml definitions"); - } - - self::$DATA_OBJECT_HANDLER = new DataObjectHandler($entityParsedData); + self::$DATA_OBJECT_HANDLER = new DataObjectHandler(); + self::$DATA_OBJECT_HANDLER->initDataObjects(); } return self::$DATA_OBJECT_HANDLER; @@ -78,11 +72,10 @@ public static function getInstance() /** * DataArrayProcessor constructor. * @constructor - * @param array $arrayData */ - private function __construct($arrayData) + private function __construct() { - $this->arrayData = $arrayData; + // private constructor } /** @@ -105,12 +98,27 @@ public function getObject($entityName) */ public function getAllObjects() { - if (!$this->data) { - $this->parseEnvVariables(); - $this->parseDataEntities(); + return $this->data; + } + + /** + * Method to initialize parsing of data.xml and read into objects. + * + * @return void + */ + private function initDataObjects() + { + $entityParser = ObjectManagerFactory::getObjectManager()->create(DataProfileSchemaParser::class); + $entityParsedData = $entityParser->readDataProfiles(); + + if (!$entityParsedData) { + trigger_error("No entities could be parsed from xml definitions", E_USER_NOTICE); + return; } - return $this->data; + $this->arrayData = $entityParsedData; + $this->parseEnvVariables(); + $this->parseDataEntities(); } /** diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php index 13273a34a..b13ae5c7b 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/PageObjectHandler.php @@ -94,7 +94,14 @@ private function initPageObjects() $objectManager = ObjectManagerFactory::getObjectManager(); /** @var $parser \Magento\FunctionalTestingFramework\XmlParser\PageParser */ $parser = $objectManager->get(PageParser::class); - foreach ($parser->getData(self::TYPE) as $pageName => $pageData) { + $parsedObjs = $parser->getData(self::TYPE); + + if (!$parsedObjs) { + trigger_error("No " . self::TYPE . " objects defined", E_USER_NOTICE); + return; + } + + foreach ($parsedObjs as $pageName => $pageData) { $urlPath = $pageData[PageObjectHandler::URL_PATH_ATTR]; $module = $pageData[PageObjectHandler::MODULE_ATTR]; $sections = array_keys($pageData[PageObjectHandler::SUB_TYPE]); diff --git a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php index 65edaeab2..8bd5863a4 100644 --- a/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Page/Handlers/SectionObjectHandler.php @@ -97,7 +97,14 @@ private function initSectionObjects() $objectManager = ObjectManagerFactory::getObjectManager(); /** @var $parser \Magento\FunctionalTestingFramework\XmlParser\SectionParser */ $parser = $objectManager->get(SectionParser::class); - foreach ($parser->getData(self::TYPE) as $sectionName => $sectionData) { + $parsedObjs = $parser->getData(self::TYPE); + + if (!$parsedObjs) { + trigger_error("No " . self::TYPE . " objects defined", E_USER_NOTICE); + return; + } + + foreach ($parsedObjs as $sectionName => $sectionData) { // create elements $elements = []; foreach ($sectionData[SectionObjectHandler::SUB_TYPE] as $elementName => $elementData) { diff --git a/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php b/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php index e721f5c5a..ab8187881 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Test/Handlers/CestObjectHandler.php @@ -131,6 +131,11 @@ private function initCestData() $cestObjectExtractor = new CestObjectExtractor(); + if (!$parsedCestArray) { + trigger_error("Could not parse any data.xml.", E_USER_NOTICE); + return; + } + foreach ($parsedCestArray[CestObjectHandler::XML_ROOT] as $cestName => $cestData) { if (!is_array($cestData)) { continue; From 9e82e0c4c482261413467353cc0632423ffeb8fc Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Mon, 16 Oct 2017 13:37:38 -0500 Subject: [PATCH 17/27] MQE-460:OperationDataArrayResolver does not correctly resolve meta data object type's key attribute. - fixed array reference to key instead of type when building request data --- .../DataGenerator/Persist/OperationDataArrayResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php index 49cb535a8..93ab204bc 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php @@ -73,7 +73,7 @@ public function resolveOperationDataArray($entityObject, $operationMetadata, $op foreach ($operationMetadata as $operationElement) { if ($operationElement->getType() == OperationElementExtractor::OPERATION_OBJECT_OBJ_NAME) { $entityObj = $this->resolveOperationObjectAndEntityData($entityObject, $operationElement->getValue()); - $operationDataArray[$operationElement->getValue()] = + $operationDataArray[$operationElement->getKey()] = $this->resolveOperationDataArray($entityObj, $operationElement->getNestedMetadata(), $operation); continue; } From 94b250a8ec23c45e68f759a859fe6b7d574edd1c Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Mon, 16 Oct 2017 13:39:09 -0500 Subject: [PATCH 18/27] MQE-426: License File and Copyright --- COPYING.txt | 9 +++++++++ LICENSE.txt | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 COPYING.txt create mode 100644 LICENSE.txt diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 000000000..d2cbcd015 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,9 @@ +Copyright © 2013-2017 Magento, Inc. + +Each Magento source file included in this distribution is licensed under OSL 3.0 or the Magento Enterprise Edition (MEE) license + +http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +Please see LICENSE.txt for the full text of the OSL 3.0 license or contact license@magentocommerce.com for a copy. + +Subject to Licensee's payment of fees and compliance with the terms and conditions of the MEE License, the MEE License supersedes the OSL 3.0 license for each source file. +Please see LICENSE_EE.txt for the full text of the MEE License or visit http://magento.com/legal/terms/enterprise. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000..36b2459f6 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. From d498246d10746deda830ab2b62147c217f3788f7 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Wed, 18 Oct 2017 10:55:19 -0500 Subject: [PATCH 19/27] MQE-431: Automatically check for copyright at the top of every framework file - Adding copyright to a file which made it to sprint-develop before this task. --- dev/tests/phpunit.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dev/tests/phpunit.xml b/dev/tests/phpunit.xml index e06631268..4751e1765 100644 --- a/dev/tests/phpunit.xml +++ b/dev/tests/phpunit.xml @@ -1,3 +1,10 @@ +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.3/phpunit.xsd" From 6118965e975da87973221a1678ba289a2951ce9a Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 18 Oct 2017 13:27:25 -0500 Subject: [PATCH 20/27] MQE-446: Unable to pass ActionGroup argument into parameterized selector - ActionGroupObject now correctly handles $datakey$ references. - Refactoring of existing methods, creation of new methods to consolidate code. --- .../Test/Objects/ActionGroupObject.php | 90 +++++++++++++++---- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php index 19cb0838c..c5beb1236 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionGroupObject.php @@ -13,7 +13,7 @@ */ class ActionGroupObject { - const VAR_ATTRIBUTES = ['userInput', 'selector', 'page']; + const VAR_ATTRIBUTES = ['userInput', 'selector', 'page', 'url']; /** * The name of the action group @@ -80,7 +80,11 @@ public function getSteps($arguments, $actionReferenceKey) private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) { $resolvedActions = []; - $regexPattern = '/{{([\w]+)/'; + + // $regexPattern match on: $matches[0] {{section.element(arg.field)}} + // $matches[1] = section.element + // $matches[2] = arg.field + $regexPattern = '/{{([\w.]+)\(*([\w.$\']+)*\)*}}/'; foreach ($this->parsedActions as $action) { $varAttributes = array_intersect(self::VAR_ATTRIBUTES, array_keys($action->getCustomActionAttributes())); @@ -90,11 +94,15 @@ private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) foreach ($varAttributes as $varAttribute) { $attributeValue = $action->getCustomActionAttributes()[$varAttribute]; preg_match_all($regexPattern, $attributeValue, $matches); - if (empty($matches[0]) & empty($matches[1])) { + + if (empty($matches[0])) { continue; } - $newActionAttributes[$varAttribute] = $this->resolveNewAttribute( + //get rid of full match {{arg.field(arg.field)}} + unset($matches[0]); + + $newActionAttributes[$varAttribute] = $this->replaceAttributeArguments( $arguments, $attributeValue, $matches @@ -114,34 +122,78 @@ private function getResolvedActionsWithArgs($arguments, $actionReferenceKey) } /** - * Function which takes an array of arguments to use for replacement of var name, the string which contains - * the variable for replacement, an array of matching vars. + * Function that takes an array of replacement arguments, and matches them with args in an actionGroup's attribute. + * Determines if the replacement arguments are persisted data, and replaces them accordingly. * * @param array $arguments * @param string $attributeValue * @param array $matches * @return string */ - private function resolveNewAttribute($arguments, $attributeValue, $matches) + private function replaceAttributeArguments($arguments, $attributeValue, $matches) { + $matchParametersKey = 2; $newAttributeVal = $attributeValue; - foreach ($matches[1] as $var) { - if (array_key_exists($var, $arguments)) { - if (preg_match('/\$\$[\w.\[\]\',]+\$\$/', $arguments[$var])) { - //if persisted $$data$$ was passed, return $$param.id$$ instead of {{$$param$$.id}} - $newAttributeVal = str_replace($var, trim($arguments[$var], '$'), $newAttributeVal); - $newAttributeVal = str_replace('{{', '$$', str_replace('}}', '$$', $newAttributeVal)); - } elseif (preg_match('/\$[\w.\[\]\',]+\$/', $arguments[$var])) { - //elseif persisted $data$ was passed, return $param.id$ instead of {{$param$.id}} - $newAttributeVal = str_replace($var, trim($arguments[$var], '$'), $newAttributeVal); - $newAttributeVal = str_replace('{{', '$', str_replace('}}', '$', $newAttributeVal)); + + foreach ($matches as $key => $match) { + foreach ($match as $variable) { + if (empty($variable)) { + continue; + } + // Truncate arg.field into arg + $variableName = strstr($variable, '.', true); + // Check if arguments has a mapping for the given variableName + if (!array_key_exists($variableName, $arguments)) { + continue; + } + $isPersisted = strstr($arguments[$variableName], '$'); + if ($isPersisted) { + $newAttributeVal = $this->replacePersistedArgument( + $arguments[$variableName], + $attributeValue, + $variable, + $variableName, + $key == $matchParametersKey ? true : false + ); } else { - //else normal param replacement - $newAttributeVal = str_replace($var, $arguments[$var], $newAttributeVal); + $newAttributeVal = str_replace($variableName, $arguments[$variableName], $attributeValue); } } } return $newAttributeVal; } + + /** + * Replaces args with replacements given, behavior is specific to persisted arguments. + * @param string $replacement + * @param string $attributeValue + * @param string $fullVariable + * @param string $variable + * @param boolean $isParameter + * @return string + */ + private function replacePersistedArgument($replacement, $attributeValue, $fullVariable, $variable, $isParameter) + { + //hookPersisted will be true if replacement passed in is $$arg.field$$, otherwise assume it's $arg.field$ + $hookPersistedArgumentRegex = '/\$\$[\w.\[\]\',]+\$\$/'; + $hookPersisted = (preg_match($hookPersistedArgumentRegex, $replacement)); + + $newAttributeValue = $attributeValue; + + $scope = '$'; + if ($hookPersisted) { + $scope = '$$'; + } + + // parameter replacements require changing of (arg.field) to ($arg.field$) + if ($isParameter) { + $newAttributeValue = str_replace($fullVariable, $scope . $fullVariable . $scope, $newAttributeValue); + } else { + $newAttributeValue = str_replace('{{', $scope, str_replace('}}', $scope, $newAttributeValue)); + } + $newAttributeValue = str_replace($variable, trim($replacement, '$'), $newAttributeValue); + + return $newAttributeValue; + } } From 8ec65856207ae3e61ce7c320b6adc671a54cc142 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 18 Oct 2017 13:52:41 -0500 Subject: [PATCH 21/27] MQE-457: Error when using literal param with space in elements (#19) - changed regex to accept all characters, not inside $data.ref$ and 'string literal` parameters. --- .../FunctionalTestingFramework/Test/Objects/ActionObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php index cc473075e..9647d0f34 100644 --- a/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php +++ b/src/Magento/FunctionalTestingFramework/Test/Objects/ActionObject.php @@ -398,7 +398,7 @@ private function matchParameterReferences($reference, $parameters) $resolvedParameters = []; foreach ($parameters as $parameter) { $parameter = trim($parameter); - preg_match_all("/[$'][\w.$]+[$']/", $parameter, $match); + preg_match_all("/[$'][\w\D]+[$']/", $parameter, $match); if (!empty($match[0])) { $resolvedParameters[] = ltrim(rtrim($parameter, "'"), "'"); } else { From 2a88898a6cf1eb95570d2a40d6d7d7fdaeb09ed5 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Mon, 16 Oct 2017 12:03:12 -0500 Subject: [PATCH 22/27] MQE-429: Implement support for api PUT requests. --- etc/di.xml | 12 +++-- .../DataGenerator/Persist/CurlHandler.php | 30 ++++++++--- .../Persist/DataPersistenceHandler.php | 36 +++++++++---- .../Persist/OperationDataArrayResolver.php | 1 - .../Test/etc/testSchema.xsd | 13 +++++ .../Util/Protocol/CurlInterface.php | 2 +- .../Util/Protocol/CurlTransport.php | 2 +- .../Util/TestGenerator.php | 51 ++++++++++++++++++- 8 files changed, 120 insertions(+), 27 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index 02c13ad01..bcde410d9 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -206,7 +206,8 @@ <item name="/config/cest/test" xsi:type="string">name</item> <item name="/config/cest/test/actionGroup/argument" xsi:type="string">name</item> <item name="/config/cest/test/createData/required-entity" xsi:type="string">createDataKey</item> - <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/test/updateData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> </argument> <argument name="fileName" xsi:type="string">*Cest.xml</argument> <argument name="defaultScope" xsi:type="string">Cest</argument> @@ -216,14 +217,17 @@ <virtualType name="Magento\FunctionalTestingFramework\Test\Config\Dom\ArrayNodeConfig" type="Magento\FunctionalTestingFramework\Config\Dom\ArrayNodeConfig"> <arguments> <argument name="assocArrayAttributes" xsi:type="array"> - <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> - <item name="/config/cest/before/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> - <item name="/config/cest/after/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/before/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/after/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> <item name="/config/cest/test" xsi:type="string">name</item> <item name="/config/cest" xsi:type="string">name</item> <item name="/config/cest/test/createData/required-entity" xsi:type="string">createDataKey</item> <item name="/config/cest/before/createData/required-entity" xsi:type="string">createDataKey</item> <item name="/config/cest/after/createData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/test/updateData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/before/updateData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/after/updateData/required-entity" xsi:type="string">createDataKey</item> <item name="/config/cest/test/actionGroup/argument" xsi:type="string">name</item> </argument> <argument name="numericArrays" xsi:type="array"> diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php index e8223b245..a4a574dd2 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/CurlHandler.php @@ -75,6 +75,7 @@ class CurlHandler /** * ApiSubObject constructor. + * * @param string $operation * @param EntityDataObject $entityObject * @param string $storeCode @@ -105,7 +106,14 @@ public function executeRequest($dependentEntities) $successRegex = null; $returnRegex = null; - $apiUrl = $this->resolveUrlReference($this->operationDefinition->getApiUrl()); + if ($this->operation == 'update') { + $entities = array_merge($dependentEntities, [$this->entityObject]); + } elseif ((null !== $dependentEntities) && is_array($dependentEntities)) { + $entities = array_merge([$this->entityObject], $dependentEntities); + } else { + $entities = [$this->entityObject]; + } + $apiUrl = $this->resolveUrlReference($this->operationDefinition->getApiUrl(), $entities); $headers = $this->operationDefinition->getHeaders(); $authorization = $this->operationDefinition->getAuth(); $contentType = $this->operationDefinition->getContentType(); @@ -175,12 +183,13 @@ public function isContentTypeJson() } /** - * Resolve rul reference from entity data. + * Resolve rul reference from entity objects. * * @param string $urlIn + * @param array $entityObjects * @return string */ - private function resolveUrlReference($urlIn) + private function resolveUrlReference($urlIn, $entityObjects) { $urlOut = $urlIn; $matchedParams = []; @@ -188,11 +197,16 @@ private function resolveUrlReference($urlIn) if (!empty($matchedParams)) { foreach ($matchedParams[0] as $paramKey => $paramValue) { - $param = $this->entityObject->getDataByName( - $matchedParams[1][$paramKey], - EntityDataObject::CEST_UNIQUE_VALUE - ); - $urlOut = str_replace($paramValue, $param, $urlIn); + foreach ($entityObjects as $entityObject) { + $param = $entityObject->getDataByName( + $matchedParams[1][$paramKey], + EntityDataObject::CEST_UNIQUE_VALUE + ); + if (null !== $param) { + $urlOut = str_replace($paramValue, $param, $urlIn); + continue; + } + } } } return $urlOut; diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php index 7c80765b7..a5f9ca175 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php @@ -69,7 +69,28 @@ public function createEntity($storeCode = null) } $curlHandler = new CurlHandler('create', $this->entityObject, $this->storeCode); $result = $curlHandler->executeRequest($this->dependentObjects); - $this->setCreatedEntity( + $this->setCreatedObject( + $result, + $curlHandler->getRequestDataArray(), + $curlHandler->isContentTypeJson() + ); + } + + /** + * Function which executes a put request based on specific operation metadata. + * + * @param string $storeCode + * @return void + */ + + public function updateEntity($storeCode = null) + { + if (!empty($storeCode)) { + $this->storeCode = $storeCode; + } + $curlHandler = new CurlHandler('update', $this->entityObject, $this->storeCode); + $result = $curlHandler->executeRequest($this->dependentObjects); + $this->setCreatedObject( $result, $curlHandler->getRequestDataArray(), $curlHandler->isContentTypeJson() @@ -92,7 +113,8 @@ public function deleteEntity($storeCode = null) } /** - * Returns the createdDataObject, instantiated when the entity is created via API. + * Returns the created data object, instantiated when the entity is created via API. + * * @return EntityDataObject */ public function getCreatedObject() @@ -110,21 +132,15 @@ public function getCreatedDataByName($dataName) return $this->createdObject->getDataByName($dataName, EntityDataObject::NO_UNIQUE_PROCESS); } - // TODO add update function - /* public function updateEntity() - { - - }*/ - /** - * Save created entity. + * Save the created data object. * * @param string|array $response * @param array $requestDataArray * @param bool $isJson * @return void */ - private function setCreatedEntity($response, $requestDataArray, $isJson) + private function setCreatedObject($response, $requestDataArray, $isJson) { if ($isJson) { $persistedData = array_merge($requestDataArray, json_decode($response, true)); diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php index 93ab204bc..852862640 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/OperationDataArrayResolver.php @@ -45,7 +45,6 @@ class OperationDataArrayResolver */ public function __construct($dependentEntities = null) { - //empty constructor if ($dependentEntities !== null) { foreach ($dependentEntities as $entity) { $this->dependentEntities[$entity->getName()] = $entity; diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index 58f455a6e..cb2247abf 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -85,6 +85,7 @@ <xs:element type="closeTabType" name="closeTab" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="conditionalClickType" name="conditionalClick" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="createDataType" name="createData" minOccurs="0" maxOccurs="unbounded"/> + <xs:element type="updateDataType" name="updateData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="deleteDataType" name="deleteData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="dontSeeType" name="dontSee" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="dontSeeCheckboxIsCheckedType" name="dontSeeCheckboxIsChecked" minOccurs="0" maxOccurs="unbounded"/> @@ -385,6 +386,18 @@ </xs:extension> </xs:simpleContent> </xs:complexType> + <xs:complexType name="updateDataType"> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element type="requiredEntityType" name="required-entity" minOccurs="0" maxOccurs="unbounded"/> + </xs:choice> + <xs:attribute type="xs:string" name="entity" use="required"/> + <xs:attribute type="xs:string" name="mergeKey" use="required"/> + <xs:attribute type="xs:string" name="createDataKey" use="required"/> + <xs:attribute type="xs:boolean" name="remove" default="false"/> + <xs:attribute type="xs:string" name="before"/> + <xs:attribute type="xs:string" name="after"/> + <xs:attribute type="xs:string" name="storeCode"/> + </xs:complexType> <xs:complexType name="deleteDataType"> <xs:simpleContent> <xs:extension base="xs:string"> diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php index 792e0c6d4..69f615133 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlInterface.php @@ -32,7 +32,7 @@ public function addOption($option, $value); * Send request to the remote server. * * @param string $url - * @param array $body + * @param array|string $body * @param string $method * @param array $headers * @return void diff --git a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php index 5f02f476d..127768e4d 100644 --- a/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php +++ b/src/Magento/FunctionalTestingFramework/Util/Protocol/CurlTransport.php @@ -118,7 +118,7 @@ public function setConfig(array $config = []) * Send request to the remote server. * * @param string $url - * @param array $body + * @param array|string $body * @param string $method * @param array $headers * @return void diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index ad6074286..cbc179d3a 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -151,7 +151,7 @@ public function createAllCestFiles($runConfig = null, $env = null) */ private function assembleCestPhp($cestObject) { - $usePhp = $this->generateUseStatementsPhp($cestObject); + $usePhp = $this->generateUseStatementsPhp(); $classAnnotationsPhp = $this->generateClassAnnotationsPhp($cestObject->getAnnotations()); $className = $cestObject->getName(); $className = str_replace(' ', '', $className); @@ -543,6 +543,53 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $testSteps .= sprintf("\t\t$%s->deleteEntity();\n", $key); } break; + case "updateData": + $entity = $customActionAttributes['entity']; + $originalEntity = null; + if (isset($customActionAttributes['createDataKey'])) { + $originalEntity = $customActionAttributes['createDataKey']; + } + $key = $steps->getMergeKey(); + //Add an informative statement to help the user debug test runs + $testSteps .= sprintf( + "\t\t$%s->amGoingTo(\"update entity that has the mergeKey: %s\");\n", + $actor, + $key + ); + //Get Entity from Static data. + $testSteps .= sprintf( + "\t\t$%s = DataObjectHandler::getInstance()->getObject(\"%s\");\n", + $entity, + $entity + ); + + if ($hookObject) { + $updateEntityFunctionCall = sprintf("\t\t\$this->%s->updateEntity(", $key); + $dataPersistenceHandlerFunctionCall = sprintf( + "\t\t\$this->%s = new DataPersistenceHandler($%s, [\$this->%s]);\n", + $key, + $entity, + $originalEntity + ); + } else { + $updateEntityFunctionCall = sprintf("\t\t\$%s->updateEntity(", $key); + $dataPersistenceHandlerFunctionCall = sprintf( + "\t\t$%s = new DataPersistenceHandler($%s, [$%s]);\n", + $key, + $entity, + $originalEntity + ); + } + + if (isset($customActionAttributes['storeCode'])) { + $updateEntityFunctionCall .= sprintf("\"%s\");\n", $customActionAttributes['storeCode']); + } else { + $updateEntityFunctionCall .= ");\n"; + } + + $testSteps .= $dataPersistenceHandlerFunctionCall; + $testSteps .= $updateEntityFunctionCall; + break; case "dontSeeCurrentUrlEquals": case "dontSeeCurrentUrlMatches": case "seeInPopup": @@ -828,7 +875,7 @@ private function generateHooksPhp($hookObjects) $dependencies = 'AcceptanceTester $I'; foreach ($hookObject->getActions() as $step) { - if ($step->getType() == "createData") { + if (($step->getType() == "createData") || ($step->getType() == "updateData")) { $hooks .= "\t/**\n"; $hooks .= sprintf("\t * @var DataPersistenceHandler $%s;\n", $step->getMergeKey()); $hooks .= "\t */\n"; From c5cf82b20ffda7cd55a6fbb5e99afda630ff478b Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Thu, 19 Oct 2017 10:44:37 -0500 Subject: [PATCH 23/27] MQE-440: Implement support for api GET requests. --- etc/di.xml | 12 ++- .../Objects/OperationDefinitionObject.php | 16 ---- .../Persist/DataPersistenceHandler.php | 38 ++++++++- .../DataGenerator/etc/dataOperation.xsd | 1 + .../Test/etc/testSchema.xsd | 13 +++ .../Util/TestGenerator.php | 85 ++++++++++++++++++- 6 files changed, 142 insertions(+), 23 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index bcde410d9..4b313bbad 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -207,7 +207,8 @@ <item name="/config/cest/test/actionGroup/argument" xsi:type="string">name</item> <item name="/config/cest/test/createData/required-entity" xsi:type="string">createDataKey</item> <item name="/config/cest/test/updateData/required-entity" xsi:type="string">createDataKey</item> - <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/test/getData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|getData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> </argument> <argument name="fileName" xsi:type="string">*Cest.xml</argument> <argument name="defaultScope" xsi:type="string">Cest</argument> @@ -217,9 +218,9 @@ <virtualType name="Magento\FunctionalTestingFramework\Test\Config\Dom\ArrayNodeConfig" type="Magento\FunctionalTestingFramework\Config\Dom\ArrayNodeConfig"> <arguments> <argument name="assocArrayAttributes" xsi:type="array"> - <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> - <item name="/config/cest/before/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> - <item name="/config/cest/after/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/test/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|getData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/before/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|getData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> + <item name="/config/cest/after/(acceptPopup|actionGroup|amOnPage|amOnUrl|appendField|assertArraySubset|attachFile|cancelPopup|checkOption|click|clickWithLeftButton|clickWithRightButton|closeTab|createData|deleteData|updateData|getData|dontSee|dontSeeCheckboxIsChecked|dontSeeCookie|dontSeeCurrentUrlEquals|dontSeeCurrentUrlMatches|dontSeeElement|dontSeeElementInDOM|dontSeeInCurrentUrl|dontSeeInField|dontSeeInPageSource|dontSeeInSource|dontSeeInTitle|dontSeeLink|dontSeeOptionIsSelected|doubleClick|dragAndDrop|entity|executeJS|fillField|formatMoney|grabAttributeFrom|grabCookie|grabFromCurrentUrl|grabMultiple|grabPageSource|grabTextFrom|grabValueFrom|loadSessionSnapshot|loginAsAdmin|makeScreenshot|maximizeWindow|moveBack|moveForward|moveMouseOver|openNewTab|pauseExecution|performOn|pressKey|reloadPage|resetCookie|resizeWindow|scrollTo|searchAndMultiSelectOption|see|seeCheckboxIsChecked|seeCookie|seeCurrentUrlEquals|seeCurrentUrlMatches|seeElement|seeElementInDOM|seeInCurrentUrl|seeInField|seeInFormFields|seeInPageSource|seeInPopup|seeInSource|seeInTitle|seeLink|seeNumberOfElements|seeOptionIsSelected|selectOption|setCookie|switchToIFrame|switchToNextTab|switchToPreviousTab|switchToWindow|typeInPopup|uncheckOption|unselectOption|wait|waitForAjaxLoad|waitForElement|waitForElementChange|waitForElementNotVisible|waitForElementVisible|waitForJS|waitForLoadingMaskToDisappear|waitForPageLoad|waitForText)" xsi:type="string">mergeKey</item> <item name="/config/cest/test" xsi:type="string">name</item> <item name="/config/cest" xsi:type="string">name</item> <item name="/config/cest/test/createData/required-entity" xsi:type="string">createDataKey</item> @@ -228,6 +229,9 @@ <item name="/config/cest/test/updateData/required-entity" xsi:type="string">createDataKey</item> <item name="/config/cest/before/updateData/required-entity" xsi:type="string">createDataKey</item> <item name="/config/cest/after/updateData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/test/getData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/before/getData/required-entity" xsi:type="string">createDataKey</item> + <item name="/config/cest/after/getData/required-entity" xsi:type="string">createDataKey</item> <item name="/config/cest/test/actionGroup/argument" xsi:type="string">name</item> </argument> <argument name="numericArrays" xsi:type="array"> diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php index 8f9ac19a4..b894592dd 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Objects/OperationDefinitionObject.php @@ -195,10 +195,6 @@ public function getApiUrl() if (!$this->apiUrl) { $this->apiUrl = $this->apiUri; - if (array_key_exists('path', $this->params)) { - $this->addPathParam(); - } - if (array_key_exists('query', $this->params)) { $this->addQueryParams(); } @@ -267,18 +263,6 @@ public function getReturnRegex() return $this->returnRegex; } - /** - * Function to append path params where necessary - * - * @return void - */ - private function addPathParam() - { - foreach ($this->params['path'] as $paramName => $paramValue) { - $this->apiUrl = $this->apiUrl . "/" . $paramValue; - } - } - /** * Function to append or add query parameters * diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php index a5f9ca175..fe1b1e037 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/Persist/DataPersistenceHandler.php @@ -71,6 +71,7 @@ public function createEntity($storeCode = null) $result = $curlHandler->executeRequest($this->dependentObjects); $this->setCreatedObject( $result, + null, $curlHandler->getRequestDataArray(), $curlHandler->isContentTypeJson() ); @@ -92,6 +93,30 @@ public function updateEntity($storeCode = null) $result = $curlHandler->executeRequest($this->dependentObjects); $this->setCreatedObject( $result, + null, + $curlHandler->getRequestDataArray(), + $curlHandler->isContentTypeJson() + ); + } + + /** + * Function which executes a get request on specific operation metadata. + * + * @param integer|null $index + * @param string $storeCode + * @return void + */ + + public function getEntity($index = null, $storeCode = null) + { + if (!empty($storeCode)) { + $this->storeCode = $storeCode; + } + $curlHandler = new CurlHandler('get', $this->entityObject, $this->storeCode); + $result = $curlHandler->executeRequest($this->dependentObjects); + $this->setCreatedObject( + $result, + $index, $curlHandler->getRequestDataArray(), $curlHandler->isContentTypeJson() ); @@ -136,14 +161,23 @@ public function getCreatedDataByName($dataName) * Save the created data object. * * @param string|array $response + * @param integer|null $index * @param array $requestDataArray * @param bool $isJson * @return void */ - private function setCreatedObject($response, $requestDataArray, $isJson) + private function setCreatedObject($response, $index, $requestDataArray, $isJson) { if ($isJson) { - $persistedData = array_merge($requestDataArray, json_decode($response, true)); + $responseData = json_decode($response, true); + if (is_array($responseData) && (null !== $index)) { + $responseData = $responseData[$index]; + } + if (is_array($responseData)) { + $persistedData = array_merge($requestDataArray, $responseData); + } else { + $persistedData = $requestDataArray; + } } else { $persistedData = array_merge( $this->convertToFlatArray($requestDataArray), diff --git a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd index eadc0e396..ef2b146ad 100644 --- a/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd +++ b/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd @@ -74,6 +74,7 @@ <xs:enumeration value="create" /> <xs:enumeration value="delete" /> <xs:enumeration value="update" /> + <xs:enumeration value="get" /> </xs:restriction> </xs:simpleType> <xs:simpleType name="authEnum" final="restriction" > diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index cb2247abf..3997ed460 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -87,6 +87,7 @@ <xs:element type="createDataType" name="createData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="updateDataType" name="updateData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="deleteDataType" name="deleteData" minOccurs="0" maxOccurs="unbounded"/> + <xs:element type="getDataType" name="getData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="dontSeeType" name="dontSee" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="dontSeeCheckboxIsCheckedType" name="dontSeeCheckboxIsChecked" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="dontSeeCookieType" name="dontSeeCookie" minOccurs="0" maxOccurs="unbounded"/> @@ -410,6 +411,18 @@ </xs:extension> </xs:simpleContent> </xs:complexType> + <xs:complexType name="getDataType"> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element type="requiredEntityType" name="required-entity" minOccurs="0" maxOccurs="unbounded"/> + </xs:choice> + <xs:attribute type="xs:string" name="entity" use="required"/> + <xs:attribute type="xs:string" name="mergeKey" use="required"/> + <xs:attribute type="xs:integer" name="index"/> + <xs:attribute type="xs:boolean" name="remove" default="false"/> + <xs:attribute type="xs:string" name="before"/> + <xs:attribute type="xs:string" name="after"/> + <xs:attribute type="xs:string" name="storeCode"/> + </xs:complexType> <xs:complexType name="dontSeeType"> <xs:simpleContent> <xs:extension base="xs:string"> diff --git a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php index cbc179d3a..86682fc2e 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestGenerator.php @@ -590,6 +590,86 @@ private function generateStepsPhp($stepsObject, $stepsData, $hookObject = false) $testSteps .= $dataPersistenceHandlerFunctionCall; $testSteps .= $updateEntityFunctionCall; break; + case "getData": + $entity = $customActionAttributes['entity']; + $key = $steps->getMergeKey(); + //Add an informative statement to help the user debug test runs + $testSteps .= sprintf( + "\t\t$%s->amGoingTo(\"get entity that has the mergeKey: %s\");\n", + $actor, + $key + ); + //Get Entity from Static data. + $testSteps .= sprintf( + "\t\t$%s = DataObjectHandler::getInstance()->getObject(\"%s\");\n", + $entity, + $entity + ); + + //HookObject End-Product needs to be created in the Class/Cest scope, + //otherwise create them in the Test scope. + //Determine if there are required-entities and create array of required-entities for merging. + $requiredEntities = []; + $requiredEntityObjects = []; + foreach ($customActionAttributes as $customAttribute) { + if (is_array($customAttribute) && $customAttribute['nodeName'] = 'required-entity') { + if ($hookObject) { + $requiredEntities [] = "\$this->" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] . + "->getName() => " . "\$this->" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] . + "->getType()"; + $requiredEntityObjects [] = '$this->' . $customAttribute + [self::REQUIRED_ENTITY_REFERENCE]; + } else { + $requiredEntities [] = "\$" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] + . "->getName() => " . "\$" . $customAttribute[self::REQUIRED_ENTITY_REFERENCE] . + "->getType()"; + $requiredEntityObjects [] = '$' . $customAttribute[self::REQUIRED_ENTITY_REFERENCE]; + } + } + } + + if ($hookObject) { + $getEntityFunctionCall = sprintf("\t\t\$this->%s->getEntity(", $key); + $dataPersistenceHandlerFunctionCall = sprintf( + "\t\t\$this->%s = new DataPersistenceHandler($%s", + $key, + $entity + ); + } else { + $getEntityFunctionCall = sprintf("\t\t\$%s->getEntity(", $key); + $dataPersistenceHandlerFunctionCall = sprintf( + "\t\t$%s = new DataPersistenceHandler($%s", + $key, + $entity + ); + } + + if (isset($customActionAttributes['index'])) { + $getEntityFunctionCall .= sprintf("%s", (int)$customActionAttributes['index']); + } else { + $getEntityFunctionCall .= 'null'; + } + + if (isset($customActionAttributes['storeCode'])) { + $getEntityFunctionCall .= sprintf(", \"%s\");\n", $customActionAttributes['storeCode']); + } else { + $getEntityFunctionCall .= ");\n"; + } + + //If required-entities are defined, reassign dataObject to not overwrite the static definition. + //Also, DataPersistenceHandler needs to be defined with customData array. + if (!empty($requiredEntities)) { + $dataPersistenceHandlerFunctionCall .= sprintf( + ", [%s]);\n", + implode(', ', $requiredEntityObjects) + ); + } else { + $dataPersistenceHandlerFunctionCall .= ");\n"; + } + + $testSteps .= $dataPersistenceHandlerFunctionCall; + $testSteps .= $getEntityFunctionCall; + break; case "dontSeeCurrentUrlEquals": case "dontSeeCurrentUrlMatches": case "seeInPopup": @@ -875,7 +955,10 @@ private function generateHooksPhp($hookObjects) $dependencies = 'AcceptanceTester $I'; foreach ($hookObject->getActions() as $step) { - if (($step->getType() == "createData") || ($step->getType() == "updateData")) { + if (($step->getType() == "createData") + || ($step->getType() == "updateData") + || ($step->getType() == "getData") + ) { $hooks .= "\t/**\n"; $hooks .= sprintf("\t * @var DataPersistenceHandler $%s;\n", $step->getMergeKey()); $hooks .= "\t */\n"; From b1abe0db9d9463245a7f2859b8510c7c0da6190d Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Thu, 19 Oct 2017 14:21:45 -0500 Subject: [PATCH 24/27] MQE-237:[Generator] Add before and after logic to suites - add new before/after tags - add new Group generator class - add verification test for new suite logic - update existing suite sample file --- composer.json | 3 +- composer.lock | 261 ++++++++++-------- dev/_suite/functionalSuite.xml | 14 + .../Resources/testSuiteGeneration1.txt | 4 + .../TestModule/Cest/sampleSuiteCest.xml | 87 ++++++ .../Tests/BasicCestGenerationTest.php | 2 +- .../Tests/SuiteGenerationTest.php | 82 ++++++ dev/tests/verification/Util/FileDiffUtil.php | 2 +- etc/di.xml | 36 ++- .../Suite/Generators/GroupClassGenerator.php | 150 ++++++++++ .../Suite/Handlers/SuiteObjectHandler.php | 206 +------------- .../Suite/Objects/SuiteObject.php | 31 ++- .../Suite/SuiteGenerator.php | 52 ++-- .../Suite/Util/SuiteObjectExtractor.php | 237 ++++++++++++++++ .../Suite/etc/sampleSuite.xml | 4 +- .../Suite/etc/suiteSchema.xsd | 30 +- .../Suite/views/SuiteClass.mustache | 54 ++++ .../views/partials/dataPersistence.mustache | 8 + .../Test/etc/testSchema.xsd | 10 +- .../Util/TestManifest.php | 3 +- 20 files changed, 920 insertions(+), 356 deletions(-) create mode 100644 dev/_suite/functionalSuite.xml create mode 100644 dev/tests/verification/Resources/testSuiteGeneration1.txt create mode 100644 dev/tests/verification/TestModule/Cest/sampleSuiteCest.xml create mode 100644 dev/tests/verification/Tests/SuiteGenerationTest.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php create mode 100644 src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache create mode 100644 src/Magento/FunctionalTestingFramework/Suite/views/partials/dataPersistence.mustache diff --git a/composer.json b/composer.json index 65c607324..427d08cbe 100755 --- a/composer.json +++ b/composer.json @@ -7,7 +7,8 @@ "php": "~7.0", "codeception/codeception": "2.2|2.3", "flow/jsonpath": ">0.2", - "fzaninotto/faker": "^1.6" + "fzaninotto/faker": "^1.6", + "mustache/mustache": "~2.5" }, "require-dev": { "squizlabs/php_codesniffer": "1.5.3", diff --git a/composer.lock b/composer.lock index 88f87805a..60d4dc14f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "4ccd9ed4f8c2fde43e5784bdc02b4064", - "content-hash": "d2c73e722ab7776b4cf486b17e2abfa7", + "content-hash": "6a845d91aa6c99cce577f96862648392", "packages": [ { "name": "behat/gherkin", @@ -64,7 +63,7 @@ "gherkin", "parser" ], - "time": "2016-10-30 11:50:56" + "time": "2016-10-30T11:50:56+00:00" }, { "name": "codeception/codeception", @@ -158,36 +157,36 @@ "functional testing", "unit testing" ], - "time": "2017-05-22 23:47:35" + "time": "2017-05-22T23:47:35+00:00" }, { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -212,7 +211,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14 21:17:01" + "time": "2017-07-22T11:58:36+00:00" }, { "name": "facebook/webdriver", @@ -264,7 +263,7 @@ "selenium", "webdriver" ], - "time": "2017-04-28 14:54:49" + "time": "2017-04-28T14:54:49+00:00" }, { "name": "flow/jsonpath", @@ -305,7 +304,7 @@ } ], "description": "JSONPath implementation for parsing, searching and flattening arrays", - "time": "2016-09-06 17:43:18" + "time": "2016-09-06T17:43:18+00:00" }, { "name": "fzaninotto/faker", @@ -355,7 +354,7 @@ "faker", "fixtures" ], - "time": "2017-08-15 16:48:10" + "time": "2017-08-15T16:48:10+00:00" }, { "name": "guzzlehttp/guzzle", @@ -420,7 +419,7 @@ "rest", "web service" ], - "time": "2017-06-22 18:50:49" + "time": "2017-06-22T18:50:49+00:00" }, { "name": "guzzlehttp/promises", @@ -471,7 +470,7 @@ "keywords": [ "promise" ], - "time": "2016-12-20 10:07:11" + "time": "2016-12-20T10:07:11+00:00" }, { "name": "guzzlehttp/psr7", @@ -536,7 +535,53 @@ "uri", "url" ], - "time": "2017-03-20 17:10:46" + "time": "2017-03-20T17:10:46+00:00" + }, + { + "name": "mustache/mustache", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/mustache.php.git", + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.11", + "phpunit/phpunit": "~3.7|~4.0|~5.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Mustache": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "A Mustache implementation in PHP.", + "homepage": "https://github.com/bobthecow/mustache.php", + "keywords": [ + "mustache", + "templating" + ], + "time": "2017-07-11T12:54:05+00:00" }, { "name": "myclabs/deep-copy", @@ -578,20 +623,20 @@ "object", "object graph" ], - "time": "2017-04-12 18:52:22" + "time": "2017-04-12T18:52:22+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { @@ -632,7 +677,7 @@ "reflection", "static analysis" ], - "time": "2015-12-27 11:43:31" + "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -677,7 +722,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-30 18:51:59" + "time": "2017-08-30T18:51:59+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -724,7 +769,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14 14:27:02" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", @@ -787,7 +832,7 @@ "spy", "stub" ], - "time": "2017-09-04 11:05:03" + "time": "2017-09-04T11:05:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -850,7 +895,7 @@ "testing", "xunit" ], - "time": "2017-04-02 07:44:40" + "time": "2017-04-02T07:44:40+00:00" }, { "name": "phpunit/php-file-iterator", @@ -897,7 +942,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03 07:40:28" + "time": "2016-10-03T07:40:28+00:00" }, { "name": "phpunit/php-text-template", @@ -938,7 +983,7 @@ "keywords": [ "template" ], - "time": "2015-06-21 13:50:34" + "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", @@ -987,7 +1032,7 @@ "keywords": [ "timer" ], - "time": "2017-02-26 11:10:40" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", @@ -1036,20 +1081,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-08-20 05:47:52" + "time": "2017-08-20T05:47:52+00:00" }, { "name": "phpunit/phpunit", - "version": "5.7.21", + "version": "5.7.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db" + "reference": "78532d5269d984660080d8e0f4c99c5c2ea65ffe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b91adfb64264ddec5a2dee9851f354aa66327db", - "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/78532d5269d984660080d8e0f4c99c5c2ea65ffe", + "reference": "78532d5269d984660080d8e0f4c99c5c2ea65ffe", "shasum": "" }, "require": { @@ -1118,7 +1163,7 @@ "testing", "xunit" ], - "time": "2017-06-21 08:11:54" + "time": "2017-10-15T06:13:55+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -1177,7 +1222,7 @@ "mock", "xunit" ], - "time": "2017-06-30 09:13:00" + "time": "2017-06-30T09:13:00+00:00" }, { "name": "psr/http-message", @@ -1227,7 +1272,7 @@ "request", "response" ], - "time": "2016-08-06 14:39:51" + "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", @@ -1274,7 +1319,7 @@ "psr", "psr-3" ], - "time": "2016-10-10 12:19:37" + "time": "2016-10-10T12:19:37+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1319,7 +1364,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04 06:30:41" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", @@ -1383,7 +1428,7 @@ "compare", "equality" ], - "time": "2017-01-29 09:50:25" + "time": "2017-01-29T09:50:25+00:00" }, { "name": "sebastian/diff", @@ -1435,7 +1480,7 @@ "keywords": [ "diff" ], - "time": "2017-05-22 07:24:03" + "time": "2017-05-22T07:24:03+00:00" }, { "name": "sebastian/environment", @@ -1485,7 +1530,7 @@ "environment", "hhvm" ], - "time": "2016-11-26 07:53:53" + "time": "2016-11-26T07:53:53+00:00" }, { "name": "sebastian/exporter", @@ -1552,7 +1597,7 @@ "export", "exporter" ], - "time": "2016-11-19 08:54:04" + "time": "2016-11-19T08:54:04+00:00" }, { "name": "sebastian/global-state", @@ -1603,7 +1648,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12 03:26:01" + "time": "2015-10-12T03:26:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -1649,7 +1694,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-02-18 15:18:39" + "time": "2017-02-18T15:18:39+00:00" }, { "name": "sebastian/recursion-context", @@ -1702,7 +1747,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19 07:33:16" + "time": "2016-11-19T07:33:16+00:00" }, { "name": "sebastian/resource-operations", @@ -1744,7 +1789,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28 20:34:47" + "time": "2015-07-28T20:34:47+00:00" }, { "name": "sebastian/version", @@ -1787,7 +1832,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03 07:35:21" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "stecman/symfony-console-completion", @@ -1832,20 +1877,20 @@ } ], "description": "Automatic BASH completion for Symfony Console Component based applications.", - "time": "2016-02-24 05:08:54" + "time": "2016-02-24T05:08:54+00:00" }, { "name": "symfony/browser-kit", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "aee7120b058c268363e606ff5fe8271da849a1b5" + "reference": "317d5bdf0127f06db7ea294186132b4f5b036839" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/aee7120b058c268363e606ff5fe8271da849a1b5", - "reference": "aee7120b058c268363e606ff5fe8271da849a1b5", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/317d5bdf0127f06db7ea294186132b4f5b036839", + "reference": "317d5bdf0127f06db7ea294186132b4f5b036839", "shasum": "" }, "require": { @@ -1889,20 +1934,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2017-07-29 21:54:42" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/console", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "d6596cb5022b6a0bd940eae54a1de78646a5fda6" + "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d6596cb5022b6a0bd940eae54a1de78646a5fda6", - "reference": "d6596cb5022b6a0bd940eae54a1de78646a5fda6", + "url": "https://api.github.com/repos/symfony/console/zipball/116bc56e45a8e5572e51eb43ab58c769a352366c", + "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c", "shasum": "" }, "require": { @@ -1957,20 +2002,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-08-27 14:52:21" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/css-selector", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "c5f5263ed231f164c58368efbce959137c7d9488" + "reference": "07447650225ca9223bd5c97180fe7c8267f7d332" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/c5f5263ed231f164c58368efbce959137c7d9488", - "reference": "c5f5263ed231f164c58368efbce959137c7d9488", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/07447650225ca9223bd5c97180fe7c8267f7d332", + "reference": "07447650225ca9223bd5c97180fe7c8267f7d332", "shasum": "" }, "require": { @@ -2010,20 +2055,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2017-07-29 21:54:42" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/debug", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "084d804fe35808eb2ef596ec83d85d9768aa6c9d" + "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/084d804fe35808eb2ef596ec83d85d9768aa6c9d", - "reference": "084d804fe35808eb2ef596ec83d85d9768aa6c9d", + "url": "https://api.github.com/repos/symfony/debug/zipball/eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd", + "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd", "shasum": "" }, "require": { @@ -2066,20 +2111,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-08-27 14:52:21" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/dom-crawler", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "d15dfaf71b65bf3affb80900470caf4451a8217e" + "reference": "40dafd42d5dad7fe5ad4e958413d92a207522ac1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d15dfaf71b65bf3affb80900470caf4451a8217e", - "reference": "d15dfaf71b65bf3affb80900470caf4451a8217e", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/40dafd42d5dad7fe5ad4e958413d92a207522ac1", + "reference": "40dafd42d5dad7fe5ad4e958413d92a207522ac1", "shasum": "" }, "require": { @@ -2122,20 +2167,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2017-08-15 13:31:09" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "54ca9520a00386f83bca145819ad3b619aaa2485" + "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/54ca9520a00386f83bca145819ad3b619aaa2485", - "reference": "54ca9520a00386f83bca145819ad3b619aaa2485", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d7ba037e4b8221956ab1e221c73c9e27e05dd423", + "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423", "shasum": "" }, "require": { @@ -2185,20 +2230,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-07-29 21:54:42" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/finder", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e" + "reference": "773e19a491d97926f236942484cb541560ce862d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/b2260dbc80f3c4198f903215f91a1ac7fe9fe09e", - "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e", + "url": "https://api.github.com/repos/symfony/finder/zipball/773e19a491d97926f236942484cb541560ce862d", + "reference": "773e19a491d97926f236942484cb541560ce862d", "shasum": "" }, "require": { @@ -2234,20 +2279,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-07-29 21:54:42" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803" + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803", - "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", "shasum": "" }, "require": { @@ -2259,7 +2304,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5-dev" + "dev-master": "1.6-dev" } }, "autoload": { @@ -2293,20 +2338,20 @@ "portable", "shim" ], - "time": "2017-06-14 15:44:48" + "time": "2017-10-11T12:05:26+00:00" }, { "name": "symfony/process", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b7666e9b438027a1ea0e1ee813ec5042d5d7f6f0" + "reference": "fdf89e57a723a29baf536e288d6e232c059697b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b7666e9b438027a1ea0e1ee813ec5042d5d7f6f0", - "reference": "b7666e9b438027a1ea0e1ee813ec5042d5d7f6f0", + "url": "https://api.github.com/repos/symfony/process/zipball/fdf89e57a723a29baf536e288d6e232c059697b1", + "reference": "fdf89e57a723a29baf536e288d6e232c059697b1", "shasum": "" }, "require": { @@ -2342,20 +2387,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-07-29 21:54:42" + "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/yaml", - "version": "v3.3.8", + "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0" + "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/1d8c2a99c80862bdc3af94c1781bf70f86bccac0", - "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0", + "url": "https://api.github.com/repos/symfony/yaml/zipball/8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", + "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", "shasum": "" }, "require": { @@ -2397,7 +2442,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-07-29 21:54:42" + "time": "2017-10-05T14:43:42+00:00" }, { "name": "webmozart/assert", @@ -2447,7 +2492,7 @@ "check", "validate" ], - "time": "2016-11-23 20:04:58" + "time": "2016-11-23T20:04:58+00:00" } ], "packages-dev": [ @@ -2488,7 +2533,7 @@ ], "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", "homepage": "https://github.com/sebastianbergmann/finder-facade", - "time": "2016-02-17 07:02:23" + "time": "2016-02-17T07:02:23+00:00" }, { "name": "sebastian/phpcpd", @@ -2538,7 +2583,7 @@ ], "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", - "time": "2017-02-05 07:48:01" + "time": "2017-02-05T07:48:01+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -2613,7 +2658,7 @@ "phpcs", "standards" ], - "time": "2014-05-01 03:07:07" + "time": "2014-05-01T03:07:07+00:00" }, { "name": "theseer/fdomdocument", @@ -2653,7 +2698,7 @@ ], "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", "homepage": "https://github.com/theseer/fDOMDocument", - "time": "2017-06-30 11:53:12" + "time": "2017-06-30T11:53:12+00:00" } ], "aliases": [], diff --git a/dev/_suite/functionalSuite.xml b/dev/_suite/functionalSuite.xml new file mode 100644 index 000000000..6dfd5d05b --- /dev/null +++ b/dev/_suite/functionalSuite.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> + <suite name="functionalSuite1"> + <include> + <group name="include"/> + <cest name="sampleSuiteCest" test="includeTest"/> + <cest name="sampleSuite4Cest"/> + </include> + <exclude> + <group name="exclude"/> + <cest name="sampleSuite5Cest" test="excludeTest"/> + </exclude> + </suite> +</suites> \ No newline at end of file diff --git a/dev/tests/verification/Resources/testSuiteGeneration1.txt b/dev/tests/verification/Resources/testSuiteGeneration1.txt new file mode 100644 index 000000000..797b3c2a1 --- /dev/null +++ b/dev/tests/verification/Resources/testSuiteGeneration1.txt @@ -0,0 +1,4 @@ +dev/tests/verification/_generated/functionalSuite1/sampleSuite3Cest.php:includeTest --env chrome +dev/tests/verification/_generated/functionalSuite1/sampleSuite5Cest.php:additionalTest --env chrome +dev/tests/verification/_generated/functionalSuite1/sampleSuiteCest.php:includeTest --env chrome +dev/tests/verification/_generated/functionalSuite1/sampleSuite4Cest.php:includeTest --env chrome diff --git a/dev/tests/verification/TestModule/Cest/sampleSuiteCest.xml b/dev/tests/verification/TestModule/Cest/sampleSuiteCest.xml new file mode 100644 index 000000000..9f09a9eee --- /dev/null +++ b/dev/tests/verification/TestModule/Cest/sampleSuiteCest.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <cest name="sampleSuiteCest"> + <test name="includeTest"> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + <test name="excludeTest"> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + </cest> + <cest name="sampleSuite2Cest"> + <annotations> + <group value="exclude"/> + </annotations> + <test name="additionalTest"> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + </cest> + <cest name="sampleSuite3Cest"> + <test name="includeTest"> + <annotations> + <group value="include"/> + </annotations> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + <test name="additionalTest"> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + </cest> + <cest name="sampleSuite4Cest"> + <test name="additionalTest"> + <annotations> + <group value="exclude"/> + </annotations> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + <test name="includeTest"> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + </cest> + <cest name="sampleSuite5Cest"> + <annotations> + <group value="include"/> + </annotations> + <test name="additionalTest"> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + <test name="excludeTest"> + <amOnPage mergeKey="testOnPage" url="/someUrl"/> + <see mergeKey="seeThePage" selector=".someSelector"/> + <click mergeKey="clickOnSomething" selector=".clickable"/> + <fillField mergeKey="fillAField" selector=".fillable"/> + </test> + </cest> +</config> \ No newline at end of file diff --git a/dev/tests/verification/Tests/BasicCestGenerationTest.php b/dev/tests/verification/Tests/BasicCestGenerationTest.php index d5644e0d4..b53223fce 100644 --- a/dev/tests/verification/Tests/BasicCestGenerationTest.php +++ b/dev/tests/verification/Tests/BasicCestGenerationTest.php @@ -39,4 +39,4 @@ public function testBasicGeneration() $diffResult = $fileDiffUtil->diffContents(); $this->assertNull($diffResult, $diffResult); } -} \ No newline at end of file +} diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php new file mode 100644 index 000000000..de405f6ce --- /dev/null +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -0,0 +1,82 @@ +<?php +/** + * Created by PhpStorm. + * User: imeron + * Date: 10/16/17 + * Time: 2:44 PM + */ + +namespace tests\verification\Tests; + +use Magento\FunctionalTestingFramework\Suite\SuiteGenerator; +use Magento\FunctionalTestingFramework\Util\TestManifest; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Yaml\Yaml; +use tests\verification\Util\FileDiffUtil; + +class SuiteGenerationTest extends TestCase +{ + const RESOURCES_DIR = TESTS_BP . DIRECTORY_SEPARATOR . 'verification' . DIRECTORY_SEPARATOR . 'Resources'; + const CONFIG_YML_FILE = FW_BP . DIRECTORY_SEPARATOR . SuiteGenerator::YAML_CODECEPTION_CONFIG_FILENAME; + + private static $YML_EXISTS_FLAG = false; + private static $TEST_GROUPS = []; + + + public static function setUpBeforeClass() + { + if (file_exists(self::CONFIG_YML_FILE)) { + self::$YML_EXISTS_FLAG = true; + return; + } + + $configYml = fopen(self::CONFIG_YML_FILE, "w"); + fclose($configYml); + } + + public function testSuiteGeneration1() + { + $groupName = 'functionalSuite1'; + + // Generate the Suite + SuiteGenerator::getInstance()->generateSuite($groupName); + + // Validate console message and add group name for later deletion + $this->expectOutputRegex('/Suite .* generated to .*/'); + self::$TEST_GROUPS[] = $groupName; + + // Validate Yaml file updated + $yml = Yaml::parse(file_get_contents(self::CONFIG_YML_FILE)); + $this->assertArrayHasKey($groupName, $yml['groups']); + + // Validate test manifest contents + $actualManifest = TESTS_BP . + DIRECTORY_SEPARATOR . + "verification" . + DIRECTORY_SEPARATOR . + "_generated" . + DIRECTORY_SEPARATOR . + $groupName . + DIRECTORY_SEPARATOR . + TestManifest::TEST_MANIFEST_FILENAME; + $expectedManifest = self::RESOURCES_DIR . DIRECTORY_SEPARATOR . __FUNCTION__ . ".txt"; + $fileDiffUtil = new FileDiffUtil($expectedManifest, $actualManifest); + $this->assertNull($fileDiffUtil->diffContents()); + } + + public static function tearDownAfterClass() + { + // 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['group'][$testGroup]); + } + + file_put_contents(self::CONFIG_YML_FILE, Yaml::dump($yml, 10)); + return; + } + + unlink(self::CONFIG_YML_FILE); + } +} diff --git a/dev/tests/verification/Util/FileDiffUtil.php b/dev/tests/verification/Util/FileDiffUtil.php index a9772eab5..0937472a1 100644 --- a/dev/tests/verification/Util/FileDiffUtil.php +++ b/dev/tests/verification/Util/FileDiffUtil.php @@ -50,4 +50,4 @@ public function diffContents() return $differingContent; } -} \ No newline at end of file +} diff --git a/etc/di.xml b/etc/di.xml index 4b313bbad..aa24e6e1c 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -338,15 +338,43 @@ <virtualType name="Magento\FunctionalTestingFramework\Config\Reader\SuiteData" type="Magento\FunctionalTestingFramework\Config\Reader\Filesystem"> <arguments> <argument name="fileResolver" xsi:type="object">Magento\FunctionalTestingFramework\Config\FileResolver\Root</argument> - <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\Converter</argument> + <argument name="converter" xsi:type="object">Magento\FunctionalTestingFramework\Config\SuiteDataConverter</argument> <argument name="schemaLocator" xsi:type="object">Magento\FunctionalTestingFramework\Config\SchemaLocator\SuiteData</argument> <argument name="idAttributes" xsi:type="array"> - <item name="/config/suite" xsi:type="string">name</item> - <item name="/config/suite/include/group" xsi:type="string">name</item> - <item name="/config/suite/exclude/group" xsi:type="string">name</item> + <item name="/suites/suite" xsi:type="string">name</item> + <item name="/suites/suite/include/group" xsi:type="string">name</item> + <item name="/suites/suite/include/cest" xsi:type="string">name</item> + <item name="/suites/suite/include/module" xsi:type="string">name</item> + <item name="/suites/suite/exclude/group" xsi:type="string">name</item> + <item name="/suites/suite/exclude/cest" xsi:type="string">name</item> + <item name="/suites/suite/exclude/module" xsi:type="string">name</item> </argument> <argument name="fileName" xsi:type="string">*.xml</argument> <argument name="defaultScope" xsi:type="string">_suite</argument> </arguments> </virtualType> + + <virtualType name="Magento\FunctionalTestingFramework\Suite\Config\Dom\SuiteArrayNodeConfig" type="Magento\FunctionalTestingFramework\Config\Dom\ArrayNodeConfig"> + <arguments> + <argument name="assocArrayAttributes" xsi:type="array"> + <item name="/suites/suite" xsi:type="string">name</item> + <item name="/suites/suite/before/(createData|deleteData)" xsi:type="string">mergeKey</item> + <item name="/suites/suite/after/(createData|deleteData)" xsi:type="string">mergeKey</item> + <item name="/suites/suite/before/createData/required-entity" xsi:type="string">createDataKey</item> + <item name="/suites/suite/after/createData/required-entity" xsi:type="string">createDataKey</item> + <item name="/suites/suite/include/group" xsi:type="string">name</item> + <item name="/suites/suite/include/cest" xsi:type="string">name</item> + <item name="/suites/suite/include/module" xsi:type="string">name</item> + <item name="/suites/suite/exclude/group" xsi:type="string">name</item> + <item name="/suites/suite/exclude/cest" xsi:type="string">name</item> + <item name="/suites/suite/exclude/module" xsi:type="string">name</item> + </argument> + </arguments> + </virtualType> + + <virtualType name="Magento\FunctionalTestingFramework\Config\SuiteDataConverter" type="Magento\FunctionalTestingFramework\Test\Config\Converter\Dom\Flat"> + <arguments> + <argument name="arrayNodeConfig" xsi:type="object">Magento\FunctionalTestingFramework\Suite\Config\Dom\SuiteArrayNodeConfig</argument> + </arguments> + </virtualType> </config> diff --git a/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php new file mode 100644 index 000000000..043c267c2 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/Generators/GroupClassGenerator.php @@ -0,0 +1,150 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\FunctionalTestingFramework\Suite\Generators; + +use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; +use Magento\FunctionalTestingFramework\Test\Objects\ActionObject; +use Magento\FunctionalTestingFramework\Test\Objects\CestHookObject; +use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor; +use Magento\FunctionalTestingFramework\Util\TestGenerator; +use Mustache_Engine; +use Mustache_Loader_FilesystemLoader; + +class GroupClassGenerator +{ + const MUSTACHE_TEMPLATE_NAME = 'SuiteClass'; + const SUITE_NAME_TAG = 'suiteName'; + const TEST_COUNT_TAG = 'testCount'; + const BEFORE_MUSTACHE_KEY = 'before'; + const AFTER_MUSTACHE_KEY = 'after'; + const ENTITY_NAME_TAG = 'entityName'; + const ENTITY_MERGE_KEY = 'mergeKey'; + const REQUIRED_ENTITY_KEY = 'requiredEntities'; + const LAST_REQUIRED_ENTITY_TAG = 'last'; + const MUSTACHE_VAR_TAG = 'var'; + + const GROUP_DIR_NAME = 'Group'; + + /** + * Mustache_Engine instance for template loading + * + * @var Mustache_Engine + */ + private $mustacheEngine; + + /** + * GroupClassGenerator constructor + */ + public function __construct() + { + $this->mustacheEngine = new Mustache_Engine([ + 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__DIR__) . DIRECTORY_SEPARATOR . "views"), + 'partials_loader' => new Mustache_Loader_FilesystemLoader( + dirname(__DIR__) . DIRECTORY_SEPARATOR . "views" . DIRECTORY_SEPARATOR . "partials" + ) + ]); + } + + /** + * Method for adding preconditions and creating a corresponding group file for codeception. After generation, + * the method returns the config path for the group file. + * + * @param SuiteObject $suiteObject + * @return string + */ + public function generateGroupClass($suiteObject) + { + $classContent = $this->createClassContent($suiteObject); + $configEntry = self::GROUP_DIR_NAME . DIRECTORY_SEPARATOR . $suiteObject->getName(); + $filePath = dirname(dirname(__DIR__)) . + DIRECTORY_SEPARATOR . + $configEntry . + '.php'; + file_put_contents($filePath, $classContent); + + return str_replace(DIRECTORY_SEPARATOR, "\\", $configEntry); + } + + /** + * Function to create group class content based on suite object definition. + * + * @param SuiteObject $suiteObject + * @return string; + */ + private function createClassContent($suiteObject) + { + $mustacheData = []; + $mustacheData[self::SUITE_NAME_TAG] = $suiteObject->getName(); + $mustacheData[self::TEST_COUNT_TAG] = count($suiteObject->getCests()); + + $mustacheData[self::BEFORE_MUSTACHE_KEY] = $this->buildHookMustacheArray($suiteObject->getBeforeHook()); + $mustacheData[self::AFTER_MUSTACHE_KEY] = $this->buildHookMustacheArray($suiteObject->getAfterHook()); + $mustacheData[self::MUSTACHE_VAR_TAG] = array_merge( + $mustacheData[self::BEFORE_MUSTACHE_KEY]['createData'] ?? [], + $mustacheData[self::AFTER_MUSTACHE_KEY]['createData'] ?? [] + ); + + return $this->mustacheEngine->render(self::MUSTACHE_TEMPLATE_NAME, $mustacheData); + } + + /** + * Function which takes hook objects and transforms data into array for mustache template engine. + * + * @param CestHookObject $hookObj + * @return array + */ + private function buildHookMustacheArray($hookObj) + { + $mustacheHookArray = []; + foreach ($hookObj->getActions() as $action) { + /** @var ActionObject $action */ + $entityArray = []; + $entityArray[self::ENTITY_MERGE_KEY] = $action->getMergeKey(); + $entityArray[self::ENTITY_NAME_TAG] = + $action->getCustomActionAttributes()['entity'] ?? + $action->getCustomActionAttributes()[TestGenerator::REQUIRED_ENTITY_REFERENCE]; + + // if there is more than 1 custom attribute, we can assume there are required entities + if (count($action->getCustomActionAttributes()) > 1) { + $entityArray[self::REQUIRED_ENTITY_KEY] = + $this->buildReqEntitiesMustacheArray($action->getCustomActionAttributes()); + } + + $mustacheHookArray[$action->getType()][] = $entityArray; + } + + return $mustacheHookArray; + } + + /** + * Function which takes any required entities under a 'createData' tag and transforms data into array to be consumed + * by mustache template. + * (<createData entity="" mergeKey=""> + * <requiredEntity'...) + * + * @param array $customAttributes + * @return array + */ + private function buildReqEntitiesMustacheArray($customAttributes) + { + $requiredEntities = []; + foreach ($customAttributes as $attribute) { + if (!is_array($attribute)) { + continue; + } + + if ($attribute[ActionObjectExtractor::NODE_NAME] == 'required-entity') { + $requiredEntities[] = [self::ENTITY_NAME_TAG => $attribute[TestGenerator::REQUIRED_ENTITY_REFERENCE]]; + } + } + + //append "last" attribute to final entry for mustache template (omit trailing comma) + $requiredEntities[count($requiredEntities)-1][self::LAST_REQUIRED_ENTITY_TAG] = true; + + return $requiredEntities; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php index 6cc754d13..806ef44fc 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Handlers/SuiteObjectHandler.php @@ -10,8 +10,11 @@ use Magento\FunctionalTestingFramework\ObjectManagerFactory; use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; use Magento\FunctionalTestingFramework\Suite\Parsers\SuiteDataParser; +use Magento\FunctionalTestingFramework\Suite\Util\SuiteObjectExtractor; use Magento\FunctionalTestingFramework\Test\Handlers\CestObjectHandler; use Magento\FunctionalTestingFramework\Test\Objects\CestObject; +use Magento\FunctionalTestingFramework\Test\Util\CestHookObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\CestObjectExtractor; use Magento\Ui\Test\Unit\Component\PagingTest; /** @@ -19,16 +22,6 @@ */ class SuiteObjectHandler implements ObjectHandlerInterface { - const SUITE_TAG_NAME = 'suite'; - const INCLUDE_TAG_NAME = 'include'; - const EXCLUDE_TAG_NAME = 'exclude'; - const MODULE_TAG_NAME = 'module'; - const MODULE_TAG_FILE_ATTRIBUTE = 'file'; - const CEST_TAG_NAME = 'cest'; - const CEST_TAG_NAME_ATTRIBUTE = 'name'; - const CEST_TAG_TEST_ATTRIBUTE = 'test'; - const GROUP_TAG_NAME = 'group'; - /** * Singleton instance of suite object handler. * @@ -98,196 +91,7 @@ public function getAllObjects() private function initSuiteData() { $suiteDataParser = ObjectManagerFactory::getObjectManager()->create(SuiteDataParser::class); - $this->suiteObjects = $this->parseSuiteDataIntoObjects($suiteDataParser->readSuiteData()); - } - - /** - * Takes an array of parsed xml and converts into an array of suite objects. - * - * @param array $parsedSuiteData - * @return array - */ - private function parseSuiteDataIntoObjects($parsedSuiteData) - { - $suiteObjects = []; - foreach ($parsedSuiteData[self::SUITE_TAG_NAME] as $parsedSuiteName => $parsedSuite) { - $includeCests = []; - $excludeCests = []; - - $groupCestsToInclude = $parsedSuite[self::INCLUDE_TAG_NAME][0] ?? []; - $groupCestsToExclude = $parsedSuite[self::EXCLUDE_TAG_NAME][0] ?? []; - - if (array_key_exists(self::CEST_TAG_NAME, $groupCestsToInclude)) { - $includeCests = $includeCests + $this->extractRelevantTests($groupCestsToInclude[self::CEST_TAG_NAME]); - } - - if (array_key_exists(self::CEST_TAG_NAME, $groupCestsToExclude)) { - $excludeCests = $excludeCests + $this->extractRelevantTests($groupCestsToExclude[self::CEST_TAG_NAME]); - } - - $includeCests = $includeCests + $this->extractGroups($groupCestsToInclude); - $excludeCests = $excludeCests + $this->extractGroups($groupCestsToExclude); - - // get tests by path (dir or file) - if (array_key_exists(self::MODULE_TAG_NAME, $groupCestsToInclude)) { - $includeCests = array_merge( - $includeCests, - $this->extractModuleAndFiles($groupCestsToInclude[self::MODULE_TAG_NAME]) - ); - } - - if (array_key_exists(self::MODULE_TAG_NAME, $groupCestsToExclude)) { - $excludeCests = array_merge( - $excludeCests, - $this->extractModuleAndFiles($groupCestsToExclude[self::MODULE_TAG_NAME]) - ); - } - - // add all cests if include cests is completely empty - if (empty($includeCests)) { - $includeCests = CestObjectHandler::getInstance()->getAllObjects(); - } - - $suiteObjects[$parsedSuiteName] = new SuiteObject($parsedSuiteName, $includeCests, $excludeCests); - } - - return $suiteObjects; - } - - /** - * Takes an array of Cests/Tests and resolves names into corresponding Cest/Test objects. - * - * @param array $suiteTestData - * @return array - */ - private function extractRelevantTests($suiteTestData) - { - $relevantCests = []; - foreach ($suiteTestData as $cestName => $cestInfo) { - $relevantCest = CestObjectHandler::getInstance()->getObject($cestName); - - if (array_key_exists(self::CEST_TAG_TEST_ATTRIBUTE, $cestInfo)) { - $relevantTest = $relevantCest->getTests()[$cestInfo[self::CEST_TAG_TEST_ATTRIBUTE]] ?? null; - - if (!$relevantTest) { - trigger_error( - "Test " . - $cestInfo[self::CEST_TAG_NAME_ATTRIBUTE] . - " does not exist.", - E_USER_NOTICE - ); - continue; - } - - $relevantCests[$cestName] = new CestObject( - $relevantCest->getName(), - $relevantCest->getAnnotations(), - [$relevantTest->getName() => $relevantTest], - $relevantCest->getHooks() - ); - } else { - $relevantCests[$cestName] = $relevantCest; - } - } - - return $relevantCests; - } - - /** - * Takes an array of group names and resolves to Cest/Test objects. - * - * @param array $suiteData - * @return array - */ - private function extractGroups($suiteData) - { - $cestsByGroup = []; - // get tests by group - if (array_key_exists(self::GROUP_TAG_NAME, $suiteData)) { - //loop groups and add to the groupCests - foreach ($suiteData[self::GROUP_TAG_NAME] as $groupName => $groupVal) { - $cestsByGroup = $cestsByGroup + CestObjectHandler::getInstance()->getCestsByGroup($groupName); - } - } - - return $cestsByGroup; - } - - /** - * Takes an array of modules/files and resolves to an array of cest objects. - * - * @param array $suitePathData - * @return array - */ - private function extractModuleAndFiles($suitePathData) - { - $cestsByModule = []; - foreach ($suitePathData as $moduleName => $fileInfo) { - if (empty($fileInfo)) { - $cestsByModule = array_merge($cestsByModule, $this->resolveModulePathCestNames($moduleName)); - } else { - $cestObj = $this->resolveFilePathCestName($fileInfo[self::MODULE_TAG_FILE_ATTRIBUTE], $moduleName); - $cestsByModule[$cestObj->getName()] = $cestObj; - } - } - - return $cestsByModule; - } - - /** - * Takes a filepath (and optionally a module name) and resolves to a cest object. - * - * @param string $filename - * @param null $moduleName - * @return CestObject - * @throws Exception - */ - private function resolveFilePathCestName($filename, $moduleName = null) - { - $filepath = $filename; - if (!strstr($filepath, DIRECTORY_SEPARATOR)) { - $filepath = TESTS_MODULE_PATH . - DIRECTORY_SEPARATOR . - $moduleName . - DIRECTORY_SEPARATOR . - 'Cest' . - DIRECTORY_SEPARATOR . - $filename; - } - - if (!file_exists($filepath)) { - throw new Exception("Could not find file ${filename}"); - } - $xml = simplexml_load_file($filepath); - $cestName = (string)$xml->cest->attributes()->name; - - return CestObjectHandler::getInstance()->getObject($cestName); - } - - /** - * Takes a single module name and resolves to an array of cests contained within specified module. - * - * @param string $moduleName - * @return array - */ - private function resolveModulePathCestNames($moduleName) - { - $cestObjects = []; - $xmlFiles = glob( - TESTS_MODULE_PATH . - DIRECTORY_SEPARATOR . - $moduleName . - DIRECTORY_SEPARATOR . - 'Cest' . - DIRECTORY_SEPARATOR . - '*.xml' - ); - - foreach ($xmlFiles as $xmlFile) { - $cestObj = $this->resolveFilePathCestName($xmlFile); - $cestObjects[$cestObj->getName()] = $cestObj; - } - - return $cestObjects; + $suiteObjectExtractor = new SuiteObjectExtractor(); + $this->suiteObjects = $suiteObjectExtractor->parseSuiteDataIntoObjects($suiteDataParser->readSuiteData()); } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php index c6fc09216..44dd64937 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php +++ b/src/Magento/FunctionalTestingFramework/Suite/Objects/SuiteObject.php @@ -5,6 +5,7 @@ */ namespace Magento\FunctionalTestingFramework\Suite\Objects; +use Magento\FunctionalTestingFramework\Test\Objects\CestHookObject; use Magento\FunctionalTestingFramework\Test\Objects\CestObject; /** @@ -33,17 +34,26 @@ class SuiteObject */ private $excludeCests = []; + /** + * Array of before/after hooks to be executed for a suite. + * + * @var array + */ + private $hooks; + /** * SuiteObject constructor. * @param string $name * @param array $includeCests * @param array $excludeCests + * @param array $hooks */ - public function __construct($name, $includeCests, $excludeCests) + public function __construct($name, $includeCests, $excludeCests, $hooks) { $this->name = $name; $this->includeCests = $includeCests; $this->excludeCests = $excludeCests; + $this->hooks = $hooks; } /** @@ -129,23 +139,34 @@ private function resolveTests($relevantTestNames, $tests) return $relevantTests; } + /** + * Convenience method for determining if a Suite will require group file generation. + * A group file will only be generated when the user specifies a before/after statement. + * + * @return bool + */ + public function requiresGroupFile() + { + return !empty($this->hooks); + } + /** * Getter for before hooks. * - * @return void + * @return CestHookObject */ public function getBeforeHook() { - //TODO implement getter for before hooks + return $this->hooks['before']; } /** * Getter for after hooks. * - * @return void + * @return CestHookObject */ public function getAfterHook() { - //TODO implement getter for after hooks + return $this->hooks['after']; } } diff --git a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php index 659b74d4b..2b6af5c5b 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php +++ b/src/Magento/FunctionalTestingFramework/Suite/SuiteGenerator.php @@ -8,6 +8,7 @@ use Magento\Framework\Phrase; use Magento\Framework\Validator\Exception; +use Magento\FunctionalTestingFramework\Suite\Generators\GroupClassGenerator; use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler; use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; use Magento\FunctionalTestingFramework\Util\Filesystem\DirSetupUtil; @@ -18,6 +19,9 @@ class SuiteGenerator { const YAML_CODECEPTION_DIST_FILENAME = 'codeception.dist.yml'; const YAML_CODECEPTION_CONFIG_FILENAME = 'codeception.yml'; + const YAML_GROUPS_TAG = 'groups'; + const YAML_EXTENSIONS_TAG = 'extensions'; + const YAML_ENABLED_TAG = 'enabled'; const YAML_COPYRIGHT_TEXT = "# Copyright © Magento, Inc. All rights reserved.\n# See COPYING.txt for license details.\n"; @@ -28,12 +32,19 @@ class SuiteGenerator */ private static $SUITE_GENERATOR_INSTANCE; + /** + * Group Class Generator initialized in constructor. + * + * @var GroupClassGenerator + */ + private $groupClassGenerator; + /** * SuiteGenerator constructor. */ private function __construct() { - //private constructor for singelton + $this->groupClassGenerator = new GroupClassGenerator(); } /** @@ -59,36 +70,35 @@ public static function getInstance() */ public function generateSuite($suiteName) { + /**@var SuiteObject $suite **/ $suite = SuiteObjectHandler::getInstance()->getObject($suiteName); - $relativePath = '_generated' . DIRECTORY_SEPARATOR . $suiteName; + $relativePath = TestGenerator::GENERATED_DIR . DIRECTORY_SEPARATOR . $suiteName; $fullPath = TESTS_MODULE_PATH . DIRECTORY_SEPARATOR . $relativePath; + $groupNamespace = null; DirSetupUtil::createGroupDir($fullPath); - $this->generateGroupFile($fullPath); $this->generateRelevantGroupTests($suiteName, $suite->getCests()); - $this->appendGroupEntryToConfig($suiteName, $fullPath); - print "Suite ${suiteName} generated to ${relativePath}.\n"; - } - /** - * Method which will be needed for adding preconditions and creating a corresponding group file for codeception. - * - * @return void - */ - private function generateGroupFile() - { - // generate group file here + if ($suite->requiresGroupFile()) { + // if the suite requires a group file, generate it and set the namespace + $groupNamespace = $this->groupClassGenerator->generateGroupClass($suite); + } + + $this->appendEntriesToConfig($suiteName, $fullPath, $groupNamespace); + print "Suite ${suiteName} generated to ${relativePath}.\n"; } /** * Function which accepts a suite name and suite path and appends a new group entry to the codeception.yml.dist - * file in order to register the set of tests as a new group. + * file in order to register the set of tests as a new group. Also appends group object location if required + * by suite. * * @param string $suiteName * @param string $suitePath + * @param string $groupNamespace * @return void */ - private function appendGroupEntryToConfig($suiteName, $suitePath) + private function appendEntriesToConfig($suiteName, $suitePath, $groupNamespace) { $configYmlPath = dirname(dirname(TESTS_BP)) . DIRECTORY_SEPARATOR; $configYmlFile = $configYmlPath . self::YAML_CODECEPTION_CONFIG_FILENAME; @@ -103,12 +113,16 @@ private function appendGroupEntryToConfig($suiteName, $suitePath) } $ymlArray = Yaml::parse($ymlContents) ?? []; + if (!array_key_exists(self::YAML_GROUPS_TAG, $ymlArray)) { + $ymlArray[self::YAML_GROUPS_TAG]= []; + } + + $ymlArray[self::YAML_GROUPS_TAG][$suiteName] = [$relativeSuitePath]; - if (!array_key_exists('group', $ymlArray)) { - $ymlArray['group']= []; + if ($groupNamespace) { + $ymlArray[self::YAML_EXTENSIONS_TAG][self::YAML_ENABLED_TAG][] = $groupNamespace; } - $ymlArray['group'][$suiteName] = $relativeSuitePath; $ymlText = self::YAML_COPYRIGHT_TEXT . Yaml::dump($ymlArray, 10); file_put_contents($configYmlFile, $ymlText); } diff --git a/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php new file mode 100644 index 000000000..04135c867 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/Util/SuiteObjectExtractor.php @@ -0,0 +1,237 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\FunctionalTestingFramework\Suite\Util; + +use Exception; +use Magento\FunctionalTestingFramework\Suite\Objects\SuiteObject; +use Magento\FunctionalTestingFramework\Test\Handlers\CestObjectHandler; +use Magento\FunctionalTestingFramework\Test\Objects\CestObject; +use Magento\FunctionalTestingFramework\Test\Util\BaseCestObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\CestHookObjectExtractor; +use Magento\FunctionalTestingFramework\Test\Util\CestObjectExtractor; + +class SuiteObjectExtractor extends BaseCestObjectExtractor +{ + const SUITE_ROOT_TAG = 'suites'; + const SUITE_TAG_NAME = 'suite'; + const INCLUDE_TAG_NAME = 'include'; + const EXCLUDE_TAG_NAME = 'exclude'; + const MODULE_TAG_NAME = 'module'; + const MODULE_TAG_FILE_ATTRIBUTE = 'file'; + const CEST_TAG_NAME = 'cest'; + const CEST_TAG_NAME_ATTRIBUTE = 'name'; + const CEST_TAG_TEST_ATTRIBUTE = 'test'; + const GROUP_TAG_NAME = 'group'; + + /** + * SuiteObjectExtractor constructor + */ + public function __construct() + { + // empty constructor + } + + /** + * Takes an array of parsed xml and converts into an array of suite objects. + * + * @param array $parsedSuiteData + * @return array + */ + public function parseSuiteDataIntoObjects($parsedSuiteData) + { + $suiteObjects = []; + $cestObjectHookExtractor = new CestHookObjectExtractor(); + foreach ($parsedSuiteData[self::SUITE_ROOT_TAG] as $parsedSuite) { + if (!is_array($parsedSuite)) { + // skip non array items parsed from suite (suite objects will always be arrays) + continue; + } + + $suiteHooks = []; + + //extract include and exclude references + $groupCestsToInclude = $parsedSuite[self::INCLUDE_TAG_NAME] ?? []; + $groupCestsToExclude = $parsedSuite[self::EXCLUDE_TAG_NAME] ?? []; + + // resolve references as cest objects + $includeCests = $this->extractCestObjectsFromSuiteRef($groupCestsToInclude); + $excludeCests = $this->extractCestObjectsFromSuiteRef($groupCestsToExclude); + + // add all cests if include cests is completely empty + if (empty($includeCests)) { + $includeCests = CestObjectHandler::getInstance()->getAllObjects(); + } + + // parse any object hooks + if (array_key_exists(CestObjectExtractor::CEST_BEFORE_HOOK, $parsedSuite)) { + $suiteHooks[CestObjectExtractor::CEST_BEFORE_HOOK] = $cestObjectHookExtractor->extractHook( + CestObjectExtractor::CEST_BEFORE_HOOK, + $parsedSuite[CestObjectExtractor::CEST_BEFORE_HOOK] + ); + } + if (array_key_exists(CestObjectExtractor::CEST_AFTER_HOOK, $parsedSuite)) { + $suiteHooks[CestObjectExtractor::CEST_AFTER_HOOK] = $cestObjectHookExtractor->extractHook( + CestObjectExtractor::CEST_AFTER_HOOK, + $parsedSuite[CestObjectExtractor::CEST_AFTER_HOOK] + ); + } + + // create the new suite object + $suiteObjects[$parsedSuite[self::NAME]] = new SuiteObject( + $parsedSuite[self::NAME], + $includeCests, + $excludeCests, + $suiteHooks + ); + } + + return $suiteObjects; + } + + /** + * Wrapper method for resolving suite reference data, checks type of suite reference and calls corresponding + * resolver for each suite reference. + * + * @param array $suiteReferences + * @return array + */ + private function extractCestObjectsFromSuiteRef($suiteReferences) + { + $cestObjectList = []; + foreach ($suiteReferences as $suiteRefName => $suiteRefData) { + if (!is_array($suiteRefData)) { + continue; + } + + switch ($suiteRefData[self::NODE_NAME]) { + case self::CEST_TAG_NAME: + $extractedCest = $this->extractRelevantCestObject($suiteRefData); + $cestObjectList[$extractedCest->getName()] = $extractedCest; + break; + case self::GROUP_TAG_NAME: + $cestObjectList = $cestObjectList + + CestObjectHandler::getInstance()->getCestsByGroup($suiteRefData[self::NAME]); + break; + case self::MODULE_TAG_NAME: + $cestObjectList = array_merge($cestObjectList, $this->extractModuleAndFiles( + $suiteRefData[self::NAME], + $suiteRefData[self::MODULE_TAG_FILE_ATTRIBUTE ?? null] + )); + break; + } + } + + return $cestObjectList; + } + + /** + * Takes an array of Cests/Tests and resolves names into corresponding Cest/Test objects. + * + * @param array $suiteTestData + * @return CestObject|null + */ + private function extractRelevantCestObject($suiteTestData) + { + $relevantCest = CestObjectHandler::getInstance()->getObject($suiteTestData[self::CEST_TAG_NAME_ATTRIBUTE]); + + if (array_key_exists(self::CEST_TAG_TEST_ATTRIBUTE, $suiteTestData)) { + $relevantTest = $relevantCest->getTests()[$suiteTestData[self::CEST_TAG_TEST_ATTRIBUTE]] ?? null; + + if (!$relevantTest) { + trigger_error( + "Test " . + $suiteTestData[self::CEST_TAG_NAME_ATTRIBUTE] . + " does not exist.", + E_USER_NOTICE + ); + return null; + } + + return new CestObject( + $relevantCest->getName(), + $relevantCest->getAnnotations(), + [$relevantTest->getName() => $relevantTest], + $relevantCest->getHooks() + ); + } + + return $relevantCest; + } + + /** + * Takes an array of modules/files and resolves to an array of cest objects. + * + * @param string $moduleName + * @param string $moduleFilePath + * @return array + */ + private function extractModuleAndFiles($moduleName, $moduleFilePath) + { + if (empty($moduleFilePath)) { + return $this->resolveModulePathCestNames($moduleName); + } + + $cestObj = $this->resolveFilePathCestName($moduleFilePath, $moduleName); + return [$cestObj->getName() => $cestObj]; + } + + /** + * Takes a filepath (and optionally a module name) and resolves to a cest object. + * + * @param string $filename + * @param null $moduleName + * @return CestObject + * @throws Exception + */ + private function resolveFilePathCestName($filename, $moduleName = null) + { + $filepath = $filename; + if (!strstr($filepath, DIRECTORY_SEPARATOR)) { + $filepath = TESTS_MODULE_PATH . + DIRECTORY_SEPARATOR . + $moduleName . + DIRECTORY_SEPARATOR . + 'Cest' . + DIRECTORY_SEPARATOR . + $filename; + } + + if (!file_exists($filepath)) { + throw new Exception("Could not find file ${filename}"); + } + $xml = simplexml_load_file($filepath); + $cestName = (string)$xml->cest->attributes()->name; + + return CestObjectHandler::getInstance()->getObject($cestName); + } + + /** + * Takes a single module name and resolves to an array of cests contained within specified module. + * + * @param string $moduleName + * @return array + */ + private function resolveModulePathCestNames($moduleName) + { + $cestObjects = []; + $xmlFiles = glob( + TESTS_MODULE_PATH . + DIRECTORY_SEPARATOR . + $moduleName . + DIRECTORY_SEPARATOR . + 'Cest' . + DIRECTORY_SEPARATOR . + '*.xml' + ); + + foreach ($xmlFiles as $xmlFile) { + $cestObj = $this->resolveFilePathCestName($xmlFile); + $cestObjects[$cestObj->getName()] = $cestObj; + } + + return $cestObjects; + } +} diff --git a/src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml b/src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml index 504050ed0..7a120b3e2 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml +++ b/src/Magento/FunctionalTestingFramework/Suite/etc/sampleSuite.xml @@ -6,7 +6,7 @@ */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="suiteSchema.xsd"> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="suiteSchema.xsd"> <suite name="mySuite"> <include> <group name="someGroup"/> @@ -20,4 +20,4 @@ <module name="moduleName" file="excludeFile"/> </exclude> </suite> -</config> +</suites> diff --git a/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd b/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd index 444228586..31ebed313 100644 --- a/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd @@ -7,8 +7,9 @@ --> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="config" type="configType"/> - <xs:complexType name="groupType"> + <xs:include schemaLocation="../../Test/etc/testSchema.xsd"/> + <xs:element name="suites" type="suiteConfigType"/> + <xs:complexType name="groupSuiteOptionType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute type="xs:string" name="name" use="required"/> @@ -16,7 +17,7 @@ </xs:extension> </xs:simpleContent> </xs:complexType> - <xs:complexType name="cestType"> + <xs:complexType name="cestSuiteOptionType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute type="xs:string" name="name" use="required"/> @@ -25,7 +26,7 @@ </xs:extension> </xs:simpleContent> </xs:complexType> - <xs:complexType name="moduleType"> + <xs:complexType name="moduleSuiteOptionType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute type="xs:string" name="name" use="optional"/> @@ -36,26 +37,33 @@ </xs:complexType> <xs:complexType name="includeType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element type="groupType" name="group" minOccurs="0"/> - <xs:element type="cestType" name="cest" minOccurs="0"/> - <xs:element type="moduleType" name="module" minOccurs="0"/> + <xs:element type="groupSuiteOptionType" name="group" minOccurs="0"/> + <xs:element type="cestSuiteOptionType" name="cest" minOccurs="0"/> + <xs:element type="moduleSuiteOptionType" name="module" minOccurs="0"/> </xs:choice> </xs:complexType> <xs:complexType name="excludeType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element type="groupType" name="group" minOccurs="0"/> - <xs:element type="cestType" name="cest" minOccurs="0"/> - <xs:element type="moduleType" name="module" minOccurs="0"/> + <xs:element type="groupSuiteOptionType" name="group" minOccurs="0"/> + <xs:element type="cestSuiteOptionType" name="cest" minOccurs="0"/> + <xs:element type="moduleSuiteOptionType" name="module" minOccurs="0"/> + </xs:choice> + </xs:complexType> + <xs:complexType name="suiteHookType"> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:group ref="dataOperationTags" maxOccurs="unbounded" minOccurs="0"/> </xs:choice> </xs:complexType> <xs:complexType name="suiteType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element type="includeType" name="include" maxOccurs="1"/> <xs:element type="excludeType" name="exclude" maxOccurs="1"/> + <xs:element type="suiteHookType" name="before" maxOccurs="1"/> + <xs:element type="suiteHookType" name="after" maxOccurs="1"/> </xs:choice> <xs:attribute type="xs:string" name="name"/> </xs:complexType> - <xs:complexType name="configType"> + <xs:complexType name="suiteConfigType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element type="suiteType" name="suite"/> </xs:choice> diff --git a/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache new file mode 100644 index 000000000..d55d6d0ee --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/views/SuiteClass.mustache @@ -0,0 +1,54 @@ +<?php + +namespace Group; + +use Magento\FunctionalTestingFramework\DataGenerator\Handlers\DataObjectHandler; +use Magento\FunctionalTestingFramework\DataGenerator\Persist\DataPersistenceHandler; + +/** + * Group class is Codeception Extension which is allowed to handle to all internal events. + * This class itself can be used to listen events for test execution of one particular group. + * It may be especially useful to create fixtures data, prepare server, etc. + * + * INSTALLATION: + * + * To use this group extension, include it to "extensions" option of global Codeception config. + */ +class {{suiteName}} extends \Codeception\GroupObject +{ + public static $group = '{{suiteName}}'; + private static $TEST_COUNT = {{testCount}}; + private static $CURRENT_TEST_RUN = 0; + + {{#var}} + private ${{mergeKey}}; + {{/var}} + + {{#before}} + public function _before(\Codeception\Event\TestEvent $e) + { + // increment test count per execution + self::$CURRENT_TEST_RUN++; + + if (self::$CURRENT_TEST_RUN == 1) { + {{> dataPersistence}} + } + } + {{/before}} + + {{#after}} + public function _after(\Codeception\Event\TestEvent $e) + { + if (self::$CURRENT_TEST_RUN == self::$TEST_COUNT) { + {{> dataPersistence}} + } + } + + public function _failed(\Codeception\Event\TestEvent $e) + { + if (self::$CURRENT_TEST_RUN == self::$TEST_COUNT) { + {{> dataPersistence}} + } + } + {{/after}} +} diff --git a/src/Magento/FunctionalTestingFramework/Suite/views/partials/dataPersistence.mustache b/src/Magento/FunctionalTestingFramework/Suite/views/partials/dataPersistence.mustache new file mode 100644 index 000000000..269204602 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Suite/views/partials/dataPersistence.mustache @@ -0,0 +1,8 @@ +{{#createData}} +${{entityName}} = DataObjectHandler::getInstance()->getObject("{{entityName}}"); +$this->{{mergeKey}} = new DataPersistenceHandler(${{entityName}}, [{{#requiredEntities}}$this->{{entityName}}{{^last}}, {{/last}}{{/requiredEntities}}]); +$this->{{mergeKey}}->createEntity(); +{{/createData}} +{{#deleteData}} +$this->{{entityName}}->deleteEntity(); +{{/deleteData}} diff --git a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd index 592aee8b7..3a31fe992 100644 --- a/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd +++ b/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd @@ -77,6 +77,7 @@ </xs:complexType> <xs:group name="actionTypeTags"> <xs:choice> + <xs:group ref="dataOperationTags"/> <xs:element type="acceptPopupType" name="acceptPopup" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="amOnPageType" name="amOnPage" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="amOnSubdomainType" name="amOnSubdomain" minOccurs="0" maxOccurs="unbounded"/> @@ -91,9 +92,7 @@ <xs:element type="closeAdminNotificationType" name="closeAdminNotification" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="closeTabType" name="closeTab" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="conditionalClickType" name="conditionalClick" minOccurs="0" maxOccurs="unbounded"/> - <xs:element type="createDataType" name="createData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="updateDataType" name="updateData" minOccurs="0" maxOccurs="unbounded"/> - <xs:element type="deleteDataType" name="deleteData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="getDataType" name="getData" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="dontSeeType" name="dontSee" minOccurs="0" maxOccurs="unbounded"/> <xs:element type="dontSeeCheckboxIsCheckedType" name="dontSeeCheckboxIsChecked" minOccurs="0" maxOccurs="unbounded"/> @@ -185,6 +184,13 @@ </xs:choice> </xs:group> + <xs:group name="dataOperationTags"> + <xs:choice> + <xs:element type="createDataType" name="createData" minOccurs="0" maxOccurs="unbounded"/> + <xs:element type="deleteDataType" name="deleteData" minOccurs="0" maxOccurs="unbounded"/> + </xs:choice> + </xs:group> + <!-- Action Group ref --> <xs:complexType name="actions"> diff --git a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php index 02146b664..4991a6644 100644 --- a/src/Magento/FunctionalTestingFramework/Util/TestManifest.php +++ b/src/Magento/FunctionalTestingFramework/Util/TestManifest.php @@ -12,6 +12,7 @@ class TestManifest { const SINGLE_RUN_CONFIG = 'singleRun'; const DEFAULT_BROWSER = 'chrome'; + const TEST_MANIFEST_FILENAME = 'testManifest.txt'; /** * Test Manifest file path. @@ -51,7 +52,7 @@ class TestManifest public function __construct($path, $runConfig, $env) { $this->relativeDirPath = substr($path, strlen(dirname(dirname(TESTS_BP))) + 1); - $filePath = $path . DIRECTORY_SEPARATOR . 'testManifest.txt'; + $filePath = $path . DIRECTORY_SEPARATOR . self::TEST_MANIFEST_FILENAME; $this->filePath = $filePath; $fileResource = fopen($filePath, 'w'); fclose($fileResource); From b4d12efba5f5eab5d72f23f200894360363360b7 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Fri, 20 Oct 2017 09:20:07 -0500 Subject: [PATCH 25/27] MQE-237:[Generator] Add before and after logic to suites - fixes from code review feedback --- dev/tests/verification/Tests/SuiteGenerationTest.php | 6 ++---- .../FunctionalTestingFramework/Group/placeholder.txt | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 src/Magento/FunctionalTestingFramework/Group/placeholder.txt diff --git a/dev/tests/verification/Tests/SuiteGenerationTest.php b/dev/tests/verification/Tests/SuiteGenerationTest.php index de405f6ce..ecba7d5cc 100644 --- a/dev/tests/verification/Tests/SuiteGenerationTest.php +++ b/dev/tests/verification/Tests/SuiteGenerationTest.php @@ -1,9 +1,7 @@ <?php /** - * Created by PhpStorm. - * User: imeron - * Date: 10/16/17 - * Time: 2:44 PM + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ namespace tests\verification\Tests; diff --git a/src/Magento/FunctionalTestingFramework/Group/placeholder.txt b/src/Magento/FunctionalTestingFramework/Group/placeholder.txt new file mode 100644 index 000000000..e55d26bb4 --- /dev/null +++ b/src/Magento/FunctionalTestingFramework/Group/placeholder.txt @@ -0,0 +1,2 @@ +This directory is used for group object files. The framework will generate files to this directory for support of +before and after logic run once (before and after) a suite of tests. \ No newline at end of file From 344cb09d502920f21f0641471c93ab33b3f90e23 Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Fri, 20 Oct 2017 09:53:30 -0500 Subject: [PATCH 26/27] MQE-237:[Generator] Add before and after logic to suites - add Group object dir to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 47a7c9c09..a405c41cb 100755 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ vendor/* _generated AcceptanceTester.php cghooks.lock +src/Magento/FunctionalTestingFramework/Group/*.php From ea1c134513b74a5c4270f77beb824a840e66ce6a Mon Sep 17 00:00:00 2001 From: Ian Meron <imeron@magento.com> Date: Fri, 20 Oct 2017 11:37:39 -0500 Subject: [PATCH 27/27] MQE-237:[Generator] Add before and after logic to suites - missed copyright tag --- dev/_suite/functionalSuite.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dev/_suite/functionalSuite.xml b/dev/_suite/functionalSuite.xml index 6dfd5d05b..62f97fdc7 100644 --- a/dev/_suite/functionalSuite.xml +++ b/dev/_suite/functionalSuite.xml @@ -1,4 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + <suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> <suite name="functionalSuite1"> <include>