InitTemplate.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. namespace Codeception;
  3. use Codeception\Command\Shared\FileSystem;
  4. use Codeception\Command\Shared\Style;
  5. use Symfony\Component\Console\Helper\QuestionHelper;
  6. use Symfony\Component\Console\Input\InputInterface;
  7. use Symfony\Component\Console\Output\OutputInterface;
  8. use Symfony\Component\Console\Question\ChoiceQuestion;
  9. use Symfony\Component\Console\Question\ConfirmationQuestion;
  10. use Symfony\Component\Console\Question\Question;
  11. /**
  12. * Codeception templates allow creating a customized setup and configuration for your project.
  13. * An abstract class for installation template. Each init template should extend it and implement a `setup` method.
  14. * Use it to build a custom setup class which can be started with `codecept init` command.
  15. *
  16. *
  17. * ```php
  18. * <?php
  19. * namespace Codeception\Template; // it is important to use this namespace so codecept init could locate this template
  20. * class CustomInstall extends \Codeception\InitTemplate
  21. * {
  22. * public function setup()
  23. * {
  24. * // implement this
  25. * }
  26. * }
  27. * ```
  28. * This class provides various helper methods for building customized setup
  29. */
  30. abstract class InitTemplate
  31. {
  32. use FileSystem;
  33. use Style;
  34. const GIT_IGNORE = '.gitignore';
  35. /**
  36. * @var string
  37. */
  38. protected $namespace = '';
  39. /**
  40. * @var string
  41. */
  42. protected $actorSuffix = 'Tester';
  43. /**
  44. * @var string
  45. */
  46. protected $workDir = '.';
  47. /**
  48. * @var InputInterface
  49. */
  50. protected $input;
  51. /**
  52. * @var OutputInterface
  53. */
  54. protected $output;
  55. public function __construct(InputInterface $input, OutputInterface $output)
  56. {
  57. $this->input = $input;
  58. $this->addStyles($output);
  59. $this->output = $output;
  60. }
  61. /**
  62. * Change the directory where Codeception should be installed.
  63. */
  64. public function initDir($workDir)
  65. {
  66. $this->checkInstalled($workDir);
  67. $this->sayInfo("Initializing Codeception in $workDir");
  68. $this->createDirectoryFor($workDir);
  69. chdir($workDir);
  70. $this->workDir = $workDir;
  71. }
  72. /**
  73. * Override this class to create customized setup.
  74. * @return mixed
  75. */
  76. abstract public function setup();
  77. /**
  78. * ```php
  79. * <?php
  80. * // propose firefox as default browser
  81. * $this->ask('select the browser of your choice', 'firefox');
  82. *
  83. * // propose firefox or chrome possible options
  84. * $this->ask('select the browser of your choice', ['firefox', 'chrome']);
  85. *
  86. * // ask true/false question
  87. * $this->ask('do you want to proceed (y/n)', true);
  88. * ```
  89. *
  90. * @param $question
  91. * @param null $answer
  92. * @return mixed|string
  93. */
  94. protected function ask($question, $answer = null)
  95. {
  96. $question = "? $question";
  97. $dialog = new QuestionHelper();
  98. if (is_array($answer)) {
  99. $question .= " <info>(" . $answer[0] . ")</info> ";
  100. return $dialog->ask($this->input, $this->output, new ChoiceQuestion($question, $answer, 0));
  101. }
  102. if (is_bool($answer)) {
  103. $question .= " (y/n) ";
  104. return $dialog->ask($this->input, $this->output, new ConfirmationQuestion($question, $answer));
  105. }
  106. if ($answer) {
  107. $question .= " <info>($answer)</info>";
  108. }
  109. return $dialog->ask($this->input, $this->output, new Question("$question ", $answer));
  110. }
  111. /**
  112. * Print a message to console.
  113. *
  114. * ```php
  115. * <?php
  116. * $this->say('Welcome to Setup');
  117. * ```
  118. *
  119. *
  120. * @param string $message
  121. */
  122. protected function say($message = '')
  123. {
  124. $this->output->writeln($message);
  125. }
  126. /**
  127. * Print a successful message
  128. * @param $message
  129. */
  130. protected function saySuccess($message)
  131. {
  132. $this->say("<notice> $message </notice>");
  133. }
  134. /**
  135. * Print warning message
  136. * @param $message
  137. */
  138. protected function sayWarning($message)
  139. {
  140. $this->say("<warning> $message </warning>");
  141. }
  142. /**
  143. * Print info message
  144. * @param $message
  145. */
  146. protected function sayInfo($message)
  147. {
  148. $this->say("<debug>> $message</debug>");
  149. }
  150. /**
  151. * Create a helper class inside a directory
  152. *
  153. * @param $name
  154. * @param $directory
  155. */
  156. protected function createHelper($name, $directory)
  157. {
  158. $file = $this->createDirectoryFor(
  159. $dir = $directory . DIRECTORY_SEPARATOR . "Helper",
  160. "$name.php"
  161. ) . "$name.php";
  162. $gen = new Lib\Generator\Helper($name, $this->namespace);
  163. // generate helper
  164. $this->createFile(
  165. $file,
  166. $gen->produce()
  167. );
  168. require_once $file;
  169. $this->sayInfo("$name helper has been created in $dir");
  170. }
  171. /**
  172. * Create an empty directory and add a placeholder file into it
  173. * @param $dir
  174. */
  175. protected function createEmptyDirectory($dir)
  176. {
  177. $this->createDirectoryFor($dir);
  178. $this->createFile($dir . DIRECTORY_SEPARATOR . '.gitkeep', '');
  179. }
  180. protected function gitIgnore($path)
  181. {
  182. if (file_exists(self::GIT_IGNORE)) {
  183. file_put_contents($path . DIRECTORY_SEPARATOR . self::GIT_IGNORE, "*\n!" . self::GIT_IGNORE);
  184. }
  185. }
  186. protected function checkInstalled($dir = '.')
  187. {
  188. if (file_exists($dir . DIRECTORY_SEPARATOR . 'codeception.yml') || file_exists($dir . DIRECTORY_SEPARATOR . 'codeception.dist.yml')) {
  189. throw new \Exception("Codeception is already installed in this directory");
  190. }
  191. }
  192. /**
  193. * Create an Actor class and generate actions for it.
  194. * Requires a suite config as array in 3rd parameter.
  195. *
  196. * @param $name
  197. * @param $directory
  198. * @param $suiteConfig
  199. */
  200. protected function createActor($name, $directory, $suiteConfig)
  201. {
  202. $file = $this->createDirectoryFor(
  203. $directory,
  204. $name
  205. ) . $this->getShortClassName($name);
  206. $file .= '.php';
  207. $suiteConfig['namespace'] = $this->namespace;
  208. $config = Configuration::mergeConfigs(Configuration::$defaultSuiteSettings, $suiteConfig);
  209. $actorGenerator = new Lib\Generator\Actor($config);
  210. $content = $actorGenerator->produce();
  211. $this->createFile($file, $content);
  212. $this->sayInfo("$name actor has been created in $directory");
  213. $actionsGenerator = new Lib\Generator\Actions($config);
  214. $content = $actionsGenerator->produce();
  215. $generatedDir = $directory . DIRECTORY_SEPARATOR . '_generated';
  216. $this->createDirectoryFor($generatedDir, 'Actions.php');
  217. $this->createFile($generatedDir . DIRECTORY_SEPARATOR . $actorGenerator->getActorName() . 'Actions.php', $content);
  218. $this->sayInfo("Actions have been loaded");
  219. }
  220. }