PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php

https://gitlab.com/crazybutterfly815/magento2
PHP | 502 lines | 212 code | 48 blank | 242 comment | 19 complexity | cc3f374c36a56da0b9ec4b1d67e52130 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Backend\Test\Block\Widget;
  7. use Magento\Mtf\Block\Block;
  8. use Magento\Mtf\Client\Locator;
  9. use Magento\Mtf\Client\Element\SimpleElement;
  10. use Magento\Mtf\Factory\Factory;
  11. /**
  12. * Abstract class Grid
  13. * Basic grid actions
  14. *
  15. * @SuppressWarnings(PHPMD.NumberOfChildren)
  16. * @SuppressWarnings(PHPMD.TooManyFields)
  17. */
  18. abstract class Grid extends Block
  19. {
  20. /**
  21. * Filters array mapping
  22. *
  23. * @var array
  24. */
  25. protected $filters = [];
  26. /**
  27. * Locator value for 'Search' button
  28. *
  29. * @var string
  30. */
  31. protected $searchButton = '[data-action="grid-filter-apply"]';
  32. /**
  33. * Locator for 'Sort' link
  34. *
  35. * @var string
  36. */
  37. protected $sortLink = "[name='%s'][title='%s']";
  38. /**
  39. * Locator value for 'Reset' button
  40. *
  41. * @var string
  42. */
  43. protected $resetButton = '[data-action="grid-filter-reset"]';
  44. /**
  45. * The first row in grid. For this moment we suggest that we should strictly define what we are going to search
  46. *
  47. * @var string
  48. */
  49. protected $rowItem = 'tbody tr';
  50. /**
  51. * The last row in the grid.
  52. *
  53. * @var string
  54. */
  55. protected $lastRowItem = 'tbody tr:last-child';
  56. /**
  57. * Locator value for link in action column
  58. *
  59. * @var string
  60. */
  61. protected $editLink = 'td[class*=col-action] a';
  62. /**
  63. * An element locator which allows to select entities in grid
  64. *
  65. * @var string
  66. */
  67. protected $selectItem = 'tbody tr [type="checkbox"]';
  68. /**
  69. * 'Select All' link
  70. *
  71. * @var string
  72. */
  73. protected $selectAll = '.massaction a[onclick*=".selectAll()"]';
  74. /**
  75. * Massaction dropdown
  76. *
  77. * @var string
  78. */
  79. protected $massactionSelect = '[id*=massaction-select]';
  80. /**
  81. * Massaction dropdown
  82. *
  83. * @var string
  84. */
  85. protected $massactionAction = '[data-menu="grid-mass-select"]';
  86. /**
  87. * Massaction 'Submit' button
  88. *
  89. * @var string
  90. */
  91. protected $massactionSubmit = '[id*=massaction-form] button';
  92. /**
  93. * Backend abstract block
  94. *
  95. * @var string
  96. */
  97. protected $templateBlock = './ancestor::body';
  98. /**
  99. * Locator type of waitForSelector
  100. *
  101. * @var Locator
  102. */
  103. protected $waitForSelectorType = Locator::SELECTOR_CSS;
  104. /**
  105. * Wait for should be for visibility or not?
  106. *
  107. * @var boolean
  108. */
  109. protected $waitForSelectorVisible = true;
  110. /**
  111. * Selector for action option select
  112. *
  113. * @var string
  114. */
  115. protected $option = '[name="status"]';
  116. /**
  117. * Active class
  118. *
  119. * @var string
  120. */
  121. protected $active = '[class=*_active]';
  122. /**
  123. * Secondary part of row locator template for getRow() method
  124. *
  125. * @var string
  126. */
  127. protected $rowTemplate = 'td[contains(.,normalize-space("%s"))]';
  128. /**
  129. * Secondary part of row locator template for getRow() method with strict option
  130. *
  131. * @var string
  132. */
  133. protected $rowTemplateStrict = 'td[text()[normalize-space()="%s"]]';
  134. /**
  135. * Magento grid loader
  136. *
  137. * @var string
  138. */
  139. protected $loader = '[data-role="spinner"]';
  140. /**
  141. * Locator for next page action
  142. *
  143. * @var string
  144. */
  145. protected $actionNextPage = '[class*=data-grid-pager] .action-next';
  146. /**
  147. * Locator for disabled next page action
  148. *
  149. * @var string
  150. */
  151. protected $actionNextPageDisabled = '[class*=data-grid-pager] .action-next.disabled';
  152. /**
  153. * First row selector
  154. *
  155. * @var string
  156. */
  157. protected $firstRowSelector = '';
  158. /**
  159. * Selector for no records row.
  160. *
  161. * @var string
  162. */
  163. protected $noRecords = '.empty-text';
  164. /**
  165. * Base part of row locator template for getRow() method.
  166. *
  167. * @var string
  168. */
  169. protected $rowPattern = '//tbody/tr[%s]';
  170. /**
  171. * Selector for confirm.
  172. *
  173. * @var string
  174. */
  175. protected $confirmModal = '.confirm._show[data-role=modal]';
  176. /**
  177. * Get backend abstract block
  178. *
  179. * @return \Magento\Backend\Test\Block\Template
  180. */
  181. protected function getTemplateBlock()
  182. {
  183. return Factory::getBlockFactory()->getMagentoBackendTemplate(
  184. $this->_rootElement->find($this->templateBlock, Locator::SELECTOR_XPATH)
  185. );
  186. }
  187. /**
  188. * Prepare data to perform search, fill in search filter
  189. *
  190. * @param array $filters
  191. * @throws \Exception
  192. */
  193. protected function prepareForSearch(array $filters)
  194. {
  195. foreach ($filters as $key => $value) {
  196. if (isset($this->filters[$key])) {
  197. $selector = $this->filters[$key]['selector'];
  198. $strategy = isset($this->filters[$key]['strategy'])
  199. ? $this->filters[$key]['strategy']
  200. : Locator::SELECTOR_CSS;
  201. $typifiedElement = isset($this->filters[$key]['input'])
  202. ? $this->filters[$key]['input']
  203. : null;
  204. $this->_rootElement->find($selector, $strategy, $typifiedElement)->setValue($value);
  205. } else {
  206. throw new \Exception("Column $key is absent in the grid or not described yet.");
  207. }
  208. }
  209. }
  210. /**
  211. * Search item via grid filter
  212. *
  213. * @param array $filter
  214. */
  215. public function search(array $filter)
  216. {
  217. $this->resetFilter();
  218. $this->prepareForSearch($filter);
  219. $this->_rootElement->find($this->searchButton, Locator::SELECTOR_CSS)->click();
  220. $this->waitLoader();
  221. }
  222. /**
  223. * Search item and open it
  224. *
  225. * @param array $filter
  226. * @throws \Exception
  227. */
  228. public function searchAndOpen(array $filter)
  229. {
  230. $this->search($filter);
  231. $rowItem = $this->getRow($filter);
  232. if ($rowItem->isVisible()) {
  233. $rowItem->find($this->editLink, Locator::SELECTOR_CSS)->click();
  234. } else {
  235. throw new \Exception("Searched item was not found by filter\n" . print_r($filter, true));
  236. }
  237. $this->waitLoader();
  238. }
  239. /**
  240. * Wait loader
  241. *
  242. * @return void
  243. */
  244. protected function waitLoader()
  245. {
  246. $this->waitForElementNotVisible($this->loader);
  247. $this->getTemplateBlock()->waitLoader();
  248. }
  249. /**
  250. * Search for item and select it
  251. *
  252. * @param array $filter
  253. * @throws \Exception
  254. */
  255. public function searchAndSelect(array $filter)
  256. {
  257. $this->search($filter);
  258. $selectItem = $this->getRow($filter)->find($this->selectItem);
  259. if ($selectItem->isVisible()) {
  260. $selectItem->click();
  261. } else {
  262. throw new \Exception("Searched item was not found by filter\n" . print_r($filter, true));
  263. }
  264. }
  265. /**
  266. * Press 'Reset' button
  267. */
  268. public function resetFilter()
  269. {
  270. $this->waitLoader();
  271. $this->_rootElement->find($this->resetButton)->click();
  272. $this->waitLoader();
  273. }
  274. /**
  275. * Perform selected massaction over checked items.
  276. *
  277. * @param array $items
  278. * @param array|string $action [array -> key = value from first select; value => value from subselect]
  279. * @param bool $acceptAlert [optional]
  280. * @param string $massActionSelection [optional]
  281. * @return void
  282. */
  283. public function massaction(array $items, $action, $acceptAlert = false, $massActionSelection = '')
  284. {
  285. if ($this->_rootElement->find($this->noRecords)->isVisible()) {
  286. return;
  287. }
  288. if (!is_array($action)) {
  289. $action = [$action => '-'];
  290. }
  291. foreach ($items as $item) {
  292. $this->searchAndSelect($item);
  293. }
  294. if ($massActionSelection) {
  295. $this->_rootElement->find($this->massactionAction, Locator::SELECTOR_CSS, 'select')
  296. ->setValue($massActionSelection);
  297. }
  298. $actionType = key($action);
  299. $this->_rootElement->find($this->massactionSelect, Locator::SELECTOR_CSS, 'select')->setValue($actionType);
  300. if (isset($action[$actionType]) && $action[$actionType] != '-') {
  301. $this->_rootElement->find($this->option, Locator::SELECTOR_CSS, 'select')->setValue($action[$actionType]);
  302. }
  303. $this->massActionSubmit($acceptAlert);
  304. }
  305. /**
  306. * Submit mass actions
  307. *
  308. * @param bool $acceptAlert
  309. * @return void
  310. */
  311. protected function massActionSubmit($acceptAlert)
  312. {
  313. $this->_rootElement->find($this->massactionSubmit, Locator::SELECTOR_CSS)->click();
  314. if ($acceptAlert) {
  315. $element = $this->browser->find($this->confirmModal);
  316. /** @var \Magento\Ui\Test\Block\Adminhtml\Modal $modal */
  317. $modal = $this->blockFactory->create(
  318. \Magento\Ui\Test\Block\Adminhtml\Modal::class,
  319. ['element' => $element]
  320. );
  321. $modal->acceptAlert();
  322. }
  323. }
  324. /**
  325. * Obtain specific row in grid
  326. *
  327. * @param array $filter
  328. * @param bool $isStrict
  329. * @return SimpleElement
  330. */
  331. protected function getRow(array $filter, $isStrict = true)
  332. {
  333. $rowTemplate = ($isStrict) ? $this->rowTemplateStrict : $this->rowTemplate;
  334. $rows = [];
  335. foreach ($filter as $value) {
  336. if (strpos($value, '"') !== false) {
  337. $rowTemplate = str_replace('"', '', $rowTemplate);
  338. $value = $this->xpathEscape($value);
  339. }
  340. $rows[] = sprintf($rowTemplate, $value);
  341. }
  342. $location = sprintf($this->rowPattern, implode(' and ', $rows));
  343. return $this->_rootElement->find($location, Locator::SELECTOR_XPATH);
  344. }
  345. /**
  346. * Get rows data
  347. *
  348. * @param array $columns
  349. * @return array
  350. */
  351. public function getRowsData(array $columns)
  352. {
  353. $data = [];
  354. do {
  355. $rows = $this->_rootElement->getElements($this->rowItem);
  356. foreach ($rows as $row) {
  357. $rowData = [];
  358. foreach ($columns as $columnName) {
  359. $rowData[$columnName] = trim($row->find('.col-' . $columnName)->getText());
  360. }
  361. $data[] = $rowData;
  362. }
  363. } while ($this->nextPage());
  364. return $data;
  365. }
  366. /**
  367. * Check if specific row exists in grid
  368. *
  369. * @param array $filter
  370. * @param bool $isSearchable
  371. * @param bool $isStrict
  372. * @return bool
  373. */
  374. public function isRowVisible(array $filter, $isSearchable = true, $isStrict = true)
  375. {
  376. $this->waitLoader();
  377. if ($isSearchable) {
  378. $this->search($filter);
  379. }
  380. return $this->getRow($filter, $isStrict)->isVisible();
  381. }
  382. /**
  383. * Sort grid by field
  384. *
  385. * @param $field
  386. * @param string $sort
  387. */
  388. public function sortGridByField($field, $sort = "desc")
  389. {
  390. $sortBlock = $this->_rootElement->find(sprintf($this->sortLink, $field, $sort));
  391. if ($sortBlock->isVisible()) {
  392. $sortBlock->click();
  393. $this->waitLoader();
  394. }
  395. }
  396. /**
  397. * Click to next page action link
  398. *
  399. * @return bool
  400. */
  401. protected function nextPage()
  402. {
  403. if ($this->_rootElement->find($this->actionNextPageDisabled)->isVisible()) {
  404. return false;
  405. }
  406. $this->_rootElement->find($this->actionNextPage)->click();
  407. $this->waitLoader();
  408. return true;
  409. }
  410. /**
  411. * Check whether first row is visible
  412. *
  413. * @return bool
  414. */
  415. public function isFirstRowVisible()
  416. {
  417. return $this->_rootElement->find($this->firstRowSelector, Locator::SELECTOR_XPATH)->isVisible();
  418. }
  419. /**
  420. * Open first item in grid
  421. *
  422. * @return void
  423. */
  424. public function openFirstRow()
  425. {
  426. $this->waitLoader();
  427. $this->_rootElement->find($this->firstRowSelector, Locator::SELECTOR_XPATH)->click();
  428. }
  429. /**
  430. * Escape single and/or double quotes in XPath selector by concat()
  431. *
  432. * @param string $query
  433. * @param string $defaultDelim [optional]
  434. * @return string
  435. */
  436. protected function xpathEscape($query, $defaultDelim = '"')
  437. {
  438. if (strpos($query, $defaultDelim) === false) {
  439. return $defaultDelim . $query . $defaultDelim;
  440. }
  441. preg_match_all("#(?:('+)|[^']+)#", $query, $matches);
  442. list($parts, $apos) = $matches;
  443. $delim = '';
  444. foreach ($parts as $i => &$part) {
  445. $delim = $apos[$i] ? '"' : "'";
  446. $part = $delim . $part . $delim;
  447. }
  448. if (count($parts) == 1) {
  449. $parts[] = $delim . $delim;
  450. }
  451. return 'concat(' . implode(',', $parts) . ')';
  452. }
  453. }