44 * See COPYING.txt for license details.
55 */
66
7+ use Symfony \Component \Yaml \Yaml ;
8+
79/** This is project's console commands configuration for Robo task runner.
810 *
911 * @codingStandardsIgnoreStart
@@ -13,6 +15,12 @@ class RoboFile extends \Robo\Tasks
1315{
1416 use Robo \Task \Base \loadShortcuts;
1517
18+ public function __construct ()
19+ {
20+ require 'tests ' . DIRECTORY_SEPARATOR . 'functional ' . DIRECTORY_SEPARATOR . '_bootstrap.php ' ;
21+ define ('VENDOR_BIN_PATH ' , PROJECT_ROOT . DIRECTORY_SEPARATOR . 'vendor ' . DIRECTORY_SEPARATOR . 'bin ' . DIRECTORY_SEPARATOR );
22+
23+ }
1624 /**
1725 * Duplicate the Example configuration files used to customize the Project for customization.
1826 *
@@ -25,6 +33,66 @@ function cloneFiles()
2533 $ this ->_exec ('cp -vf tests ' . DIRECTORY_SEPARATOR .'functional.suite.dist.yml tests ' . DIRECTORY_SEPARATOR .'functional.suite.yml ' );
2634 }
2735
36+ /**
37+ * Finds relative paths between codeception.yml file and MFTF path, and overwrites the default paths.
38+ *
39+ * @return void
40+ */
41+ private function buildCodeceptionPaths ()
42+ {
43+ $ relativePathFunc = function ($ from , $ to )
44+ {
45+ $ from = is_dir ($ from ) ? rtrim ($ from , '\/ ' ) . '/ ' : $ from ;
46+ $ to = is_dir ($ to ) ? rtrim ($ to , '\/ ' ) . '/ ' : $ to ;
47+ $ from = str_replace ('\\' , '/ ' , $ from );
48+ $ to = str_replace ('\\' , '/ ' , $ to );
49+
50+ $ from = explode ('/ ' , $ from );
51+ $ to = explode ('/ ' , $ to );
52+ $ relPath = $ to ;
53+
54+ foreach ($ from as $ depth => $ dir ) {
55+ // find first non-matching dir
56+ if ($ dir === $ to [$ depth ]) {
57+ // ignore this directory
58+ array_shift ($ relPath );
59+ } else {
60+ // get number of remaining dirs to $from
61+ $ remaining = count ($ from ) - $ depth ;
62+ if ($ remaining > 1 ) {
63+ // add traversals up to first matching dir
64+ $ padLength = (count ($ relPath ) + $ remaining - 1 ) * -1 ;
65+ $ relPath = array_pad ($ relPath , $ padLength , '.. ' );
66+ break ;
67+ } else {
68+ $ relPath [0 ] = './ ' . $ relPath [0 ];
69+ }
70+ }
71+ }
72+ return implode ('/ ' , $ relPath );
73+ };
74+
75+ //Find travel path from codeception.yml to FW_BP
76+ $ configYmlPath = dirname (dirname (TESTS_BP )) . DIRECTORY_SEPARATOR ;
77+ $ relativePath = call_user_func ($ relativePathFunc , $ configYmlPath , FW_BP );
78+ $ configYmlFile = $ configYmlPath . "codeception.yml " ;
79+ $ defaultConfigYmlFile = $ configYmlPath . "codeception.dist.yml " ;
80+
81+ if (file_exists ($ configYmlFile )) {
82+ $ ymlContents = file_get_contents ($ configYmlFile );
83+ } else {
84+ $ ymlContents = file_get_contents ($ defaultConfigYmlFile );
85+ }
86+ $ ymlArray = Yaml::parse ($ ymlContents ) ?? [];
87+ if (!array_key_exists ("paths " , $ ymlArray )) {
88+ $ ymlArray ["paths " ] = [];
89+ }
90+ $ ymlArray ["paths " ]["support " ] = $ relativePath . 'src/Magento/FunctionalTestingFramework ' ;
91+ $ ymlArray ["paths " ]["envs " ] = $ relativePath . 'etc/_envs ' ;
92+ $ ymlText = Yaml::dump ($ ymlArray , 10 );
93+ file_put_contents ($ configYmlFile , $ ymlText );
94+ }
95+
2896 /**
2997 * Duplicate the Example configuration files for the Project.
3098 * Build the Codeception project.
@@ -34,29 +102,110 @@ function cloneFiles()
34102 function buildProject ()
35103 {
36104 $ this ->cloneFiles ();
37- $ this ->_exec ('vendor ' . DIRECTORY_SEPARATOR .'bin ' . DIRECTORY_SEPARATOR .'codecept build ' );
105+ $ this ->buildCodeceptionPaths ();
106+ $ this ->_exec (VENDOR_BIN_PATH .'codecept build ' );
38107 }
39108
40109 /**
41- * Generate all Tests in PHP.
110+ * Generate all Tests in PHP OR Generate set of tests via passing array of tests
42111 *
112+ * @param array $tests
43113 * @param array $opts
44114 * @return void
45115 */
46- function generateTests ($ opts = ['config ' => null , 'force ' => false , 'nodes ' => null ])
116+ function generateTests (array $ tests , $ opts = [
117+ 'config ' => null ,
118+ 'force ' => false ,
119+ 'nodes ' => null ,
120+ 'lines ' => 500 ,
121+ 'tests ' => null
122+ ])
47123 {
48- $ GLOBALS ['GENERATE_TESTS ' ] = true ;
124+ require 'tests ' . DIRECTORY_SEPARATOR . 'functional ' . DIRECTORY_SEPARATOR . '_bootstrap.php ' ;
125+ $ testConfiguration = $ this ->createTestConfiguration ($ tests , $ opts );
49126
50- if ($ opts ['force ' ])
51- {
52- $ GLOBALS ['FORCE_PHP_GENERATE ' ] = true ;
127+ // maintain backwards compatability for devops by not removing the nodes option yet
128+ $ lines = $ opts ['lines ' ];
129+
130+ // create our manifest file here
131+ $ testManifest = \Magento \FunctionalTestingFramework \Util \Manifest \TestManifestFactory::makeManifest ($ opts ['config ' ],$ testConfiguration ['suites ' ]);
132+ \Magento \FunctionalTestingFramework \Util \TestGenerator::getInstance (null , $ testConfiguration ['tests ' ])->createAllTestFiles ($ testManifest );
133+
134+ if ($ opts ['config ' ] == 'parallel ' ) {
135+ $ testManifest ->createTestGroups ($ lines );
53136 }
54137
55- require 'tests ' . DIRECTORY_SEPARATOR . 'functional ' . DIRECTORY_SEPARATOR . '_bootstrap.php ' ;
56- \Magento \FunctionalTestingFramework \Util \TestGenerator::getInstance ()->createAllTestFiles ($ opts ['config ' ], $ opts ['nodes ' ]);
138+ \Magento \FunctionalTestingFramework \Suite \SuiteGenerator::getInstance ()->generateAllSuites ($ testManifest );
139+ $ testManifest ->generate ();
140+
57141 $ this ->say ("Generate Tests Command Run " );
58142 }
59143
144+
145+ /**
146+ * Function which builds up a configuration including test and suites for consumption of Magento generation methods.
147+ *
148+ * @param array $tests
149+ * @param array $opts
150+ * @return array
151+ */
152+ private function createTestConfiguration ($ tests , $ opts )
153+ {
154+ // set our application configuration so we can references the user options in our framework
155+ Magento \FunctionalTestingFramework \Config \MftfApplicationConfig::create (
156+ $ opts ['force ' ],
157+ Magento \FunctionalTestingFramework \Config \MftfApplicationConfig::GENERATION_PHASE ,
158+ $ opts ['verbose ' ]
159+ );
160+
161+ $ testConfiguration = [];
162+ $ testConfiguration ['tests ' ] = $ tests ;
163+ $ testConfiguration ['suites ' ] = [];
164+
165+ $ testConfiguration = $ this ->parseTestsConfigJson ($ opts ['tests ' ], $ testConfiguration );
166+
167+ // if we have references to specific tests, we resolve the test objects and pass them to the config
168+ if (!empty ($ testConfiguration ['tests ' ]))
169+ {
170+ $ testObjects = [];
171+
172+ foreach ($ testConfiguration ['tests ' ] as $ test )
173+ {
174+ $ testObjects [$ test ] = Magento \FunctionalTestingFramework \Test \Handlers \TestObjectHandler::getInstance ()->getObject ($ test );
175+ }
176+
177+ $ testConfiguration ['tests ' ] = $ testObjects ;
178+ }
179+
180+ return $ testConfiguration ;
181+ }
182+
183+ /**
184+ * Function which takes a json string of potential custom configuration and parses/validates the resulting json
185+ * passed in by the user. The result is a testConfiguration array.
186+ *
187+ * @param string $json
188+ * @param array $testConfiguration
189+ * @return array
190+ */
191+ private function parseTestsConfigJson ($ json , $ testConfiguration ) {
192+ if ($ json == null ) {
193+ return $ testConfiguration ;
194+ }
195+
196+ $ jsonTestConfiguration = [];
197+ $ testConfigArray = json_decode ($ json , true );
198+
199+ // stop execution if we have failed to properly parse any json
200+ if (json_last_error () != JSON_ERROR_NONE ) {
201+ throw new \Magento \FunctionalTestingFramework \Exceptions \TestFrameworkException ("JSON could not be parsed: " . json_last_error_msg ());
202+ }
203+
204+ $ jsonTestConfiguration ['tests ' ] = $ testConfigArray ['tests ' ] ?? null ;;
205+ $ jsonTestConfiguration ['suites ' ] = $ testConfigArray ['suites ' ] ?? null ;
206+ return $ jsonTestConfiguration ;
207+ }
208+
60209 /**
61210 * Generate a suite based on name(s) passed in as args.
62211 *
@@ -70,7 +219,6 @@ function generateSuite(array $args)
70219 throw new Exception ("Please provide suite name(s) after generate:suite command " );
71220 }
72221
73- require 'tests ' . DIRECTORY_SEPARATOR . 'functional ' . DIRECTORY_SEPARATOR . '_bootstrap.php ' ;
74222 $ sg = \Magento \FunctionalTestingFramework \Suite \SuiteGenerator::getInstance ();
75223
76224 foreach ($ args as $ arg ) {
@@ -85,18 +233,18 @@ function generateSuite(array $args)
85233 */
86234 function functional ()
87235 {
88- $ this ->_exec (' . ' . DIRECTORY_SEPARATOR . ' vendor ' . DIRECTORY_SEPARATOR . ' bin ' . DIRECTORY_SEPARATOR . ' codecept run functional --skip-group skip ' );
236+ $ this ->_exec (VENDOR_BIN_PATH . ' codecept run functional ' );
89237 }
90238
91239 /**
92- * Run all Tests with the specified @group tag, excluding @group 'skip '.
240+ * Run all Tests with the specified @group tag'.
93241 *
94242 * @param string $args
95243 * @return void
96244 */
97245 function group ($ args = '' )
98246 {
99- $ this ->taskExec (' . ' . DIRECTORY_SEPARATOR . ' vendor ' . DIRECTORY_SEPARATOR . ' bin ' . DIRECTORY_SEPARATOR . ' codecept run functional --verbose --steps --skip-group skip --group ' )->args ($ args )->run ();
247+ $ this ->taskExec (VENDOR_BIN_PATH . ' codecept run functional --verbose --steps --group ' )->args ($ args )->run ();
100248 }
101249
102250 /**
@@ -107,7 +255,7 @@ function group($args = '')
107255 */
108256 function folder ($ args = '' )
109257 {
110- $ this ->taskExec (' . ' . DIRECTORY_SEPARATOR . ' vendor ' . DIRECTORY_SEPARATOR . ' bin ' . DIRECTORY_SEPARATOR . 'codecept run functional ' )->args ($ args )->run ();
258+ $ this ->taskExec (VENDOR_BIN_PATH . 'codecept run functional ' )->args ($ args )->run ();
111259 }
112260
113261 /**
@@ -117,7 +265,7 @@ function folder($args = '')
117265 */
118266 function example ()
119267 {
120- $ this ->_exec (' . ' . DIRECTORY_SEPARATOR . ' vendor ' . DIRECTORY_SEPARATOR . ' bin ' . DIRECTORY_SEPARATOR . ' codecept run --group example --skip-group skip ' );
268+ $ this ->_exec (VENDOR_BIN_PATH . ' codecept run --group example ' );
121269 }
122270
123271 /**
0 commit comments