PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Sylius/Bundle/ResourceBundle/Behat/DefaultContext.php

https://gitlab.com/lcp0578/Sylius
PHP | 525 lines | 263 code | 76 blank | 186 comment | 24 complexity | e26e247d7c1acf7f05cc73c62483f55c MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Sylius package.
  4. *
  5. * (c) Paweł Jędrzejewski
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sylius\Bundle\ResourceBundle\Behat;
  11. use Behat\Behat\Context\Context;
  12. use Behat\Mink\Element\NodeElement;
  13. use Behat\Mink\Exception\ElementNotFoundException;
  14. use Behat\MinkExtension\Context\RawMinkContext;
  15. use Behat\Symfony2Extension\Context\KernelAwareContext;
  16. use Doctrine\Common\Collections\ArrayCollection;
  17. use Doctrine\Common\Persistence\ObjectManager;
  18. use Faker\Factory as FakerFactory;
  19. use Faker\Generator;
  20. use Sylius\Component\Resource\Repository\RepositoryInterface;
  21. use Symfony\Component\DependencyInjection\ContainerInterface;
  22. use Symfony\Component\HttpKernel\KernelInterface;
  23. use Symfony\Component\Intl\Intl;
  24. use Symfony\Component\Security\Core\SecurityContextInterface;
  25. use Symfony\Component\Security\Core\User\UserInterface;
  26. abstract class DefaultContext extends RawMinkContext implements Context, KernelAwareContext
  27. {
  28. /**
  29. * @var string
  30. */
  31. protected $applicationName = 'sylius';
  32. /**
  33. * @var Generator
  34. */
  35. protected $faker;
  36. /**
  37. * @var array
  38. */
  39. protected $actions = array(
  40. 'viewing' => 'show',
  41. 'creation' => 'create',
  42. 'editing' => 'update',
  43. 'building' => 'build',
  44. );
  45. /**
  46. * @var KernelInterface
  47. */
  48. protected $kernel;
  49. public function __construct($applicationName = null)
  50. {
  51. \Locale::setDefault('en');
  52. $this->faker = FakerFactory::create();
  53. if (null !== $applicationName) {
  54. $this->applicationName = $applicationName;
  55. }
  56. }
  57. /**
  58. * {@inheritdoc}
  59. */
  60. public function setKernel(KernelInterface $kernel)
  61. {
  62. $this->kernel = $kernel;
  63. }
  64. /**
  65. * @param string $type
  66. * @param string $name
  67. *
  68. * @return object
  69. */
  70. protected function findOneByName($type, $name)
  71. {
  72. return $this->findOneBy($type, array('name' => trim($name)));
  73. }
  74. /**
  75. * @param string $type
  76. * @param array $criteria
  77. *
  78. * @return object
  79. *
  80. * @throws \InvalidArgumentException
  81. */
  82. protected function findOneBy($type, array $criteria)
  83. {
  84. $resource = $this
  85. ->getRepository($type)
  86. ->findOneBy($criteria)
  87. ;
  88. if (null === $resource) {
  89. throw new \InvalidArgumentException(
  90. sprintf('%s for criteria "%s" was not found.', str_replace('_', ' ', ucfirst($type)), serialize($criteria))
  91. );
  92. }
  93. return $resource;
  94. }
  95. /**
  96. * @param string $resourceName
  97. *
  98. * @return RepositoryInterface
  99. */
  100. protected function getRepository($resourceName)
  101. {
  102. return $this->getService($this->applicationName.'.repository.'.$resourceName);
  103. }
  104. /**
  105. * @return ObjectManager
  106. */
  107. protected function getEntityManager()
  108. {
  109. return $this->getService('doctrine')->getManager();
  110. }
  111. /**
  112. * @return ContainerInterface
  113. */
  114. protected function getContainer()
  115. {
  116. return $this->kernel->getContainer();
  117. }
  118. /**
  119. * @param string $id
  120. *
  121. * @return object
  122. */
  123. protected function getService($id)
  124. {
  125. return $this->getContainer()->get($id);
  126. }
  127. /**
  128. * Configuration converter.
  129. *
  130. * @param string $configurationString
  131. *
  132. * @return array
  133. */
  134. protected function getConfiguration($configurationString)
  135. {
  136. $configuration = array();
  137. $list = explode(',', $configurationString);
  138. foreach ($list as $parameter) {
  139. list($key, $value) = explode(':', $parameter, 2);
  140. $key = strtolower(trim(str_replace(' ', '_', $key)));
  141. switch ($key) {
  142. case 'country':
  143. $isoName = $this->getCountryCodeByEnglishCountryName(trim($value));
  144. $configuration[$key] = $this->getRepository('country')->findOneBy(array('isoName' => $isoName))->getId();
  145. break;
  146. case 'taxons':
  147. $configuration[$key] = new ArrayCollection(array($this->getRepository('taxon')->findOneBy(array('name' => trim($value)))->getId()));
  148. break;
  149. case 'variant':
  150. $configuration[$key] = $this->getRepository('product')->findOneBy(array('name' => trim($value)))->getMasterVariant()->getId();
  151. break;
  152. case 'amount':
  153. $configuration[$key] = (int) $value;
  154. break;
  155. default:
  156. $configuration[$key] = trim($value);
  157. break;
  158. }
  159. }
  160. return $configuration;
  161. }
  162. /**
  163. * Generate page url.
  164. * This method uses simple convention where page argument is prefixed
  165. * with the application name and used as route name passed to router generate method.
  166. *
  167. * @param object|string $page
  168. * @param array $parameters
  169. *
  170. * @return string
  171. */
  172. protected function generatePageUrl($page, array $parameters = array())
  173. {
  174. if (is_object($page)) {
  175. return $this->generateUrl($page, $parameters);
  176. }
  177. $route = str_replace(' ', '_', trim($page));
  178. $routes = $this->getContainer()->get('router')->getRouteCollection();
  179. if (null === $routes->get($route)) {
  180. $route = $this->applicationName.'_'.$route;
  181. }
  182. if (null === $routes->get($route)) {
  183. $route = str_replace($this->applicationName.'_', $this->applicationName.'_backend_', $route);
  184. }
  185. $route = str_replace(array_keys($this->actions), array_values($this->actions), $route);
  186. $route = str_replace(' ', '_', $route);
  187. return $this->generateUrl($route, $parameters);
  188. }
  189. /**
  190. * Get current user instance.
  191. *
  192. * @return UserInterface|null
  193. *
  194. * @throws \Exception
  195. */
  196. protected function getUser()
  197. {
  198. $token = $this->getSecurityContext()->getToken();
  199. if (null === $token) {
  200. throw new \Exception('No token found in security context.');
  201. }
  202. return $token->getUser();
  203. }
  204. /**
  205. * @return SecurityContextInterface
  206. */
  207. protected function getSecurityContext()
  208. {
  209. return $this->getContainer()->get('security.context');
  210. }
  211. /**
  212. * @param string $route
  213. * @param array $parameters
  214. * @param Boolean $absolute
  215. *
  216. * @return string
  217. */
  218. protected function generateUrl($route, array $parameters = array(), $absolute = false)
  219. {
  220. return $this->locatePath($this->getService('router')->generate($route, $parameters, $absolute));
  221. }
  222. /**
  223. * Presses button with specified id|name|title|alt|value.
  224. *
  225. * @param string $button
  226. *
  227. * @throws ElementNotFoundException
  228. */
  229. protected function pressButton($button)
  230. {
  231. $this->getSession()->getPage()->pressButton($this->fixStepArgument($button));
  232. }
  233. /**
  234. * Clicks link with specified id|title|alt|text.
  235. *
  236. * @param string $link
  237. *
  238. * @throws ElementNotFoundException
  239. */
  240. protected function clickLink($link)
  241. {
  242. $this->getSession()->getPage()->clickLink($this->fixStepArgument($link));
  243. }
  244. /**
  245. * Fills in form field with specified id|name|label|value.
  246. *
  247. * @param string $field
  248. * @param string $value
  249. *
  250. * @throws ElementNotFoundException
  251. */
  252. protected function fillField($field, $value)
  253. {
  254. $this->getSession()->getPage()->fillField(
  255. $this->fixStepArgument($field),
  256. $this->fixStepArgument($value)
  257. );
  258. }
  259. /**
  260. * Selects option in select field with specified id|name|label|value.
  261. *
  262. * @param string $select
  263. * @param string $option
  264. *
  265. * @throws ElementNotFoundException
  266. */
  267. protected function selectOption($select, $option)
  268. {
  269. $this->getSession()->getPage()->selectFieldOption(
  270. $this->fixStepArgument($select),
  271. $this->fixStepArgument($option)
  272. );
  273. }
  274. /**
  275. * Returns fixed step argument (with \\" replaced back to ").
  276. *
  277. * @param string $argument
  278. *
  279. * @return string
  280. */
  281. protected function fixStepArgument($argument)
  282. {
  283. return str_replace('\\"', '"', $argument);
  284. }
  285. /**
  286. * @param NodeElement $table
  287. * @param string $columnName
  288. *
  289. * @return integer
  290. *
  291. * @throws \Exception If column was not found
  292. */
  293. protected function getColumnIndex(NodeElement $table, $columnName)
  294. {
  295. $rows = $table->findAll('css', 'tr');
  296. if (!isset($rows[0])) {
  297. throw new \Exception("There are no rows!");
  298. }
  299. /** @var NodeElement $firstRow */
  300. $firstRow = $rows[0];
  301. $columns = $firstRow->findAll('css', 'th,td');
  302. foreach ($columns as $index => $column) {
  303. /** @var NodeElement $column */
  304. if (0 === stripos($column->getText(), $columnName)) {
  305. return $index;
  306. }
  307. }
  308. throw new \Exception(sprintf('Column with name "%s" not found!', $columnName));
  309. }
  310. /**
  311. * @param NodeElement $table
  312. * @param array $fields
  313. *
  314. * @return NodeElement|null
  315. *
  316. * @throws \Exception If column was not found
  317. */
  318. protected function getRowWithFields(NodeElement $table, array $fields)
  319. {
  320. $foundRows = $this->getRowsWithFields($table, $fields, true);
  321. if (empty($foundRows)) {
  322. return null;
  323. }
  324. return current($foundRows);
  325. }
  326. /**
  327. * @param NodeElement $table
  328. * @param array $fields
  329. * @param boolean $onlyFirstOccurence
  330. *
  331. * @return NodeElement[]
  332. *
  333. * @throws \Exception If columns or rows were not found
  334. */
  335. protected function getRowsWithFields(NodeElement $table, array $fields, $onlyFirstOccurence = false)
  336. {
  337. $rows = $table->findAll('css', 'tr');
  338. if (!isset($rows[0])) {
  339. throw new \Exception("There are no rows!");
  340. }
  341. $fields = $this->replaceColumnNamesWithColumnIds($table, $fields);
  342. $foundRows = array();
  343. /** @var NodeElement[] $rows */
  344. $rows = $table->findAll('css', 'tr');
  345. foreach ($rows as $row) {
  346. $found = true;
  347. /** @var NodeElement[] $columns */
  348. $columns = $row->findAll('css', 'th,td');
  349. foreach ($fields as $index => $searchedValue) {
  350. if (!isset($columns[$index])) {
  351. throw new \InvalidArgumentException(sprintf('There is no column with index %d', $index));
  352. }
  353. $containing = false;
  354. $searchedValue = trim($searchedValue);
  355. if (0 === strpos($searchedValue, '%') && (strlen($searchedValue) - 1) === strrpos($searchedValue, '%')) {
  356. $searchedValue = substr($searchedValue, 1, strlen($searchedValue) - 2);
  357. $containing = true;
  358. }
  359. $position = stripos(trim($columns[$index]->getText()), $searchedValue);
  360. if (($containing && false === $position) || (!$containing && 0 !== $position)) {
  361. $found = false;
  362. break;
  363. }
  364. }
  365. if ($found) {
  366. $foundRows[] = $row;
  367. if ($onlyFirstOccurence) {
  368. break;
  369. }
  370. }
  371. }
  372. return $foundRows;
  373. }
  374. /**
  375. * @param NodeElement $table
  376. * @param string[] $fields
  377. *
  378. * @return string[]
  379. *
  380. * @throws \Exception
  381. */
  382. protected function replaceColumnNamesWithColumnIds(NodeElement $table, array $fields)
  383. {
  384. $replacedFields = array();
  385. foreach ($fields as $columnName => $expectedValue) {
  386. $columnIndex = $this->getColumnIndex($table, $columnName);
  387. $replacedFields[$columnIndex] = $expectedValue;
  388. }
  389. return $replacedFields;
  390. }
  391. /**
  392. * Callback can return any value, to stop waiting use anonymous function provided as first argument.
  393. *
  394. * @param callable $callback
  395. * @param int $limit
  396. * @param int $delay In miliseconds
  397. *
  398. * @return mixed
  399. *
  400. * @throws \RuntimeException If timeout was reached
  401. */
  402. protected function waitFor(callable $callback, $limit = 10, $delay = 100)
  403. {
  404. for ($i = 0; $i < $limit; ++$i) {
  405. $payload = $callback();
  406. if (null !== $payload) {
  407. return $payload;
  408. }
  409. usleep($delay * 1000);
  410. }
  411. throw new \RuntimeException(sprintf('Timeout reached (%f seconds)!', round($limit * $delay / 1000, 1)));
  412. }
  413. /**
  414. * @param string $name
  415. *
  416. * @return string
  417. *
  418. * @throws \InvalidArgumentException If name is not found in country code registry.
  419. */
  420. protected function getCountryCodeByEnglishCountryName($name)
  421. {
  422. $names = Intl::getRegionBundle()->getCountryNames('en');
  423. $isoName = array_search(trim($name), $names);
  424. if (null === $isoName) {
  425. throw new \InvalidArgumentException(sprintf(
  426. 'Country "%s" not found! Available names: %s.', $name, join(', ', $names)
  427. ));
  428. }
  429. return $isoName;
  430. }
  431. /**
  432. * @param string $name
  433. *
  434. * @return string
  435. *
  436. * @throws \InvalidArgumentException If name is not found in locale code registry.
  437. */
  438. protected function getLocaleCodeByEnglishLocaleName($name)
  439. {
  440. $names = Intl::getLocaleBundle()->getLocaleNames('en');
  441. $code = array_search(trim($name), $names);
  442. if (null === $code) {
  443. throw new \InvalidArgumentException(sprintf(
  444. 'Locale "%s" not found! Available names: %s.', $name, join(', ', $names)
  445. ));
  446. }
  447. return $code;
  448. }
  449. }