PageRenderTime 47ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php

https://gitlab.com/crazybutterfly815/magento2
PHP | 495 lines | 398 code | 17 blank | 80 comment | 1 complexity | 92d578128f5096001f6b871476b31f38 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Catalog\Test\Block\Product\View;
  7. use Magento\Mtf\Block\Form;
  8. use Magento\Mtf\Client\Locator;
  9. use Magento\Mtf\Fixture\FixtureInterface;
  10. use Magento\Mtf\Fixture\InjectableFixture;
  11. use Magento\Mtf\Client\Element\SimpleElement;
  12. /**
  13. * Class CustomOptions
  14. * Form of custom options product
  15. */
  16. class CustomOptions extends Form
  17. {
  18. /**
  19. * Selector for options context
  20. *
  21. * @var string
  22. */
  23. protected $optionsContext = '#product-options-wrapper';
  24. /**
  25. * Selector for single option block
  26. *
  27. * @var string
  28. */
  29. protected $optionElement = '#product-options-wrapper > * > .field';
  30. /**
  31. * Selector for title of option
  32. *
  33. * @var string
  34. */
  35. protected $title = 'label > span:nth-child(1), legend > span:nth-child(1)';
  36. /**
  37. * Selector for required option
  38. *
  39. * @var string
  40. */
  41. protected $required = './self::*[contains(@class,"required")]';
  42. /**
  43. * Selector for price notice of option
  44. *
  45. * @var string
  46. */
  47. protected $priceNotice = './/*[@class="price-notice"]';
  48. /**
  49. * Selector for max characters of option
  50. *
  51. * @var string
  52. */
  53. protected $maxCharacters = './/div[@class="control"]/p[@class="note"]/strong';
  54. /**
  55. * Selector for label of option value element
  56. *
  57. * @var string
  58. */
  59. protected $optionLabel = './/div[@class="control"]//label[contains(@for, "options_")][%d]';
  60. /**
  61. * Select note of option by number
  62. *
  63. * @var string
  64. */
  65. protected $noteByNumber = './/*[@class="note"][%d]/strong';
  66. /**
  67. * Selector for select element of option
  68. *
  69. * @var string
  70. */
  71. protected $selectOption = './/div[@class="control"]/select';
  72. /**
  73. * Selector for option of select element
  74. *
  75. * @var string
  76. */
  77. protected $option = './/option[%d]';
  78. /**
  79. * Option XPath locator by value
  80. *
  81. * @var string
  82. */
  83. protected $optionByValueLocator = '//*[@class="product-options-wrapper"]//option[text()="%s"]/..';
  84. /**
  85. * Select XPath locator by title
  86. *
  87. * @var string
  88. */
  89. protected $selectByTitleLocator = '//*[*[@class="product-options-wrapper"]//span[text()="%s"]]//select';
  90. /**
  91. * Select XPath locator by option name
  92. *
  93. * @var string
  94. */
  95. protected $optionByName = '//*[label[contains(.,"%s")] or legend[contains(.,"%s")]]';
  96. /**
  97. * Get product options
  98. *
  99. * @param FixtureInterface $product
  100. * @return array
  101. * @throws \Exception
  102. */
  103. public function getOptions(FixtureInterface $product)
  104. {
  105. $dataOptions = $product->hasData('custom_options')
  106. ? $product->getDataFieldConfig('custom_options')['source']->getCustomOptions()
  107. : [];
  108. $listCustomOptions = $this->getListOptions();
  109. $result = [];
  110. foreach ($dataOptions as $option) {
  111. $title = $option['title'];
  112. if (!isset($listCustomOptions[$title])) {
  113. throw new \Exception("Can't find option: \"{$title}\"");
  114. }
  115. /** @var SimpleElement $optionElement */
  116. $optionElement = $listCustomOptions[$title];
  117. $typeMethod = preg_replace('/[^a-zA-Z]/i', '', $this->getOptionType($option['type']));
  118. $getTypeData = 'get' . ucfirst(strtolower($typeMethod)) . 'Data';
  119. $optionData = $this->$getTypeData($optionElement);
  120. $optionData['title'] = $title;
  121. $optionData['type'] = $option['type'];
  122. $optionData['is_require'] = $optionElement->find($this->required, Locator::SELECTOR_XPATH)->isVisible()
  123. ? 'Yes'
  124. : 'No';
  125. $result[$title] = $optionData;
  126. }
  127. return ['custom_options' => $result];
  128. }
  129. /**
  130. * Get list custom options
  131. *
  132. * @return array
  133. */
  134. protected function getListOptions()
  135. {
  136. $customOptions = [];
  137. $optionElements = $this->_rootElement->getElements($this->optionElement);
  138. foreach ($optionElements as $optionElement) {
  139. $title = $optionElement->find($this->title)->getText();
  140. $customOptions[$title] = $optionElement;
  141. }
  142. return $customOptions;
  143. }
  144. /**
  145. * Get data of "Field" custom option
  146. *
  147. * @param SimpleElement $option
  148. * @return array
  149. */
  150. protected function getFieldData(SimpleElement $option)
  151. {
  152. $price = $this->getOptionPriceNotice($option);
  153. $maxCharacters = $option->find($this->maxCharacters, Locator::SELECTOR_XPATH);
  154. return [
  155. 'options' => [
  156. [
  157. 'price' => floatval($price),
  158. 'max_characters' => $maxCharacters->isVisible() ? $maxCharacters->getText() : null,
  159. ],
  160. ]
  161. ];
  162. }
  163. /**
  164. * Get data of "Area" custom option
  165. *
  166. * @param SimpleElement $option
  167. * @return array
  168. */
  169. protected function getAreaData(SimpleElement $option)
  170. {
  171. return $this->getFieldData($option);
  172. }
  173. /**
  174. * Get data of "File" custom option
  175. *
  176. * @param SimpleElement $option
  177. * @return array
  178. */
  179. protected function getFileData(SimpleElement $option)
  180. {
  181. $price = $this->getOptionPriceNotice($option);
  182. return [
  183. 'options' => [
  184. [
  185. 'price' => floatval($price),
  186. 'file_extension' => $this->getOptionNotice($option, 1),
  187. 'image_size_x' => preg_replace('/[^0-9]/', '', $this->getOptionNotice($option, 2)),
  188. 'image_size_y' => preg_replace('/[^0-9]/', '', $this->getOptionNotice($option, 3)),
  189. ],
  190. ]
  191. ];
  192. }
  193. /**
  194. * Get data of "Drop-down" custom option
  195. *
  196. * @param SimpleElement $option
  197. * @return array
  198. */
  199. protected function getDropdownData(SimpleElement $option)
  200. {
  201. $select = $option->find($this->selectOption, Locator::SELECTOR_XPATH, 'select');
  202. // Skip "Choose option ..."(option #1)
  203. return $this->getSelectOptionsData($select, 2);
  204. }
  205. /**
  206. * Get data of "Multiple Select" custom option
  207. *
  208. * @param SimpleElement $option
  209. * @return array
  210. */
  211. protected function getMultipleSelectData(SimpleElement $option)
  212. {
  213. $multiselect = $option->find($this->selectOption, Locator::SELECTOR_XPATH, 'multiselect');
  214. return $this->getSelectOptionsData($multiselect, 1);
  215. }
  216. /**
  217. * Get data of "Radio Buttons" custom option
  218. *
  219. * @param SimpleElement $option
  220. * @return array
  221. */
  222. protected function getRadioButtonsData(SimpleElement $option)
  223. {
  224. $listOptions = [];
  225. $count = 1;
  226. /** @var SimpleElement $option */
  227. $option = $option->find(sprintf($this->optionLabel, $count), Locator::SELECTOR_XPATH);
  228. while ($option->isVisible()) {
  229. $listOptions[] = $this->parseOptionText($option->getText());
  230. ++$count;
  231. $option = $option->find(sprintf($this->optionLabel, $count), Locator::SELECTOR_XPATH);
  232. }
  233. return [
  234. 'options' => $listOptions
  235. ];
  236. }
  237. /**
  238. * Get data of "Checkbox" custom option
  239. *
  240. * @param SimpleElement $option
  241. * @return array
  242. */
  243. protected function getCheckboxData(SimpleElement $option)
  244. {
  245. return $this->getRadioButtonsData($option);
  246. }
  247. /**
  248. * Get data of "Date" custom option
  249. *
  250. * @param SimpleElement $option
  251. * @return array
  252. */
  253. protected function getDateData(SimpleElement $option)
  254. {
  255. $price = $this->getOptionPriceNotice($option);
  256. return [
  257. 'options' => [
  258. [
  259. 'price' => floatval($price),
  260. ],
  261. ]
  262. ];
  263. }
  264. /**
  265. * Get data of "Date & Time" custom option
  266. *
  267. * @param SimpleElement $option
  268. * @return array
  269. */
  270. protected function getDateTimeData(SimpleElement $option)
  271. {
  272. return $this->getDateData($option);
  273. }
  274. /**
  275. * Get data of "Time" custom option
  276. *
  277. * @param SimpleElement $option
  278. * @return array
  279. */
  280. protected function getTimeData(SimpleElement $option)
  281. {
  282. return $this->getDateData($option);
  283. }
  284. /**
  285. * Get data from option of select and multiselect
  286. *
  287. * @param SimpleElement $element
  288. * @param int $firstOption
  289. * @return array
  290. */
  291. protected function getSelectOptionsData(SimpleElement $element, $firstOption = 1)
  292. {
  293. $listOptions = [];
  294. $count = $firstOption;
  295. $selectOption = $element->find(sprintf($this->option, $count), Locator::SELECTOR_XPATH);
  296. $index = 0;
  297. while ($selectOption->isVisible()) {
  298. $listOptions[$index] = $this->parseOptionText($selectOption->getText());
  299. $listOptions[$index]['sort_order'] = $index;
  300. ++$count;
  301. $selectOption = $element->find(sprintf($this->option, $count), Locator::SELECTOR_XPATH);
  302. $index++;
  303. }
  304. return [
  305. 'options' => $listOptions
  306. ];
  307. }
  308. /**
  309. * Get price from price-notice of custom option
  310. *
  311. * @param SimpleElement $option
  312. * @return array
  313. */
  314. protected function getOptionPriceNotice(SimpleElement $option)
  315. {
  316. $priceNotice = $option->find($this->priceNotice, Locator::SELECTOR_XPATH);
  317. if (!$priceNotice->isVisible()) {
  318. return null;
  319. }
  320. return preg_replace('/[^0-9\.]/', '', $priceNotice->getText());
  321. }
  322. /**
  323. * Get notice of option by number
  324. *
  325. * @param SimpleElement $option
  326. * @param int $number
  327. * @return mixed
  328. */
  329. protected function getOptionNotice(SimpleElement $option, $number)
  330. {
  331. $note = $option->find(sprintf($this->noteByNumber, $number), Locator::SELECTOR_XPATH);
  332. return $note->isVisible() ? $note->getText() : null;
  333. }
  334. /**
  335. * Parse option text to title and price
  336. *
  337. * @param string $optionText
  338. * @return array
  339. */
  340. protected function parseOptionText($optionText)
  341. {
  342. preg_match('`^(.*?) \+ ?\$([\d\.,]*?)$`', $optionText, $match);
  343. $optionPrice = isset($match[2]) ? str_replace(',', '', $match[2]) : 0;
  344. $optionTitle = isset($match[1]) ? trim($match[1]) : $optionText;
  345. return [
  346. 'title' => $optionTitle,
  347. 'price' => $optionPrice
  348. ];
  349. }
  350. /**
  351. * Fill custom options
  352. *
  353. * @param array $checkoutData
  354. * @return void
  355. */
  356. public function fillCustomOptions(array $checkoutData)
  357. {
  358. $checkoutOptions = $this->prepareOptions($checkoutData);
  359. $this->fillOptions($checkoutOptions);
  360. }
  361. /**
  362. * Prepare composite fields in checkout options data
  363. *
  364. * @param array $options
  365. * @return array
  366. */
  367. protected function prepareOptions(array $options)
  368. {
  369. $result = [];
  370. foreach ($options as $key => $option) {
  371. switch ($this->getOptionType($option['type'])) {
  372. case 'datetime':
  373. list($day, $month, $year, $hour, $minute, $dayPart) = explode('/', $option['value']);
  374. $option['value'] = [
  375. 'day' => $day,
  376. 'month' => $month,
  377. 'year' => $year,
  378. 'hour' => $hour,
  379. 'minute' => $minute,
  380. 'day_part' => $dayPart,
  381. ];
  382. break;
  383. case 'date':
  384. list($day, $month, $year) = explode('/', $option['value']);
  385. $option['value'] = [
  386. 'day' => $day,
  387. 'month' => $month,
  388. 'year' => $year,
  389. ];
  390. break;
  391. case 'time':
  392. list($hour, $minute, $dayPart) = explode('/', $option['value']);
  393. $option['value'] = [
  394. 'hour' => $hour,
  395. 'minute' => $minute,
  396. 'day_part' => $dayPart,
  397. ];
  398. break;
  399. }
  400. $result[$key] = $option;
  401. }
  402. return $result;
  403. }
  404. /**
  405. * Fill product options
  406. *
  407. * @param array $options
  408. * @return void
  409. */
  410. protected function fillOptions(array $options)
  411. {
  412. foreach ($options as $option) {
  413. $optionBlock = $this->_rootElement->find(
  414. sprintf($this->optionByName, $option['title'], $option['title']),
  415. Locator::SELECTOR_XPATH
  416. );
  417. $type = $this->getOptionType($option['type']);
  418. $mapping = $this->dataMapping([$type => $option['value']]);
  419. if ('radiobuttons' == $type || 'checkbox' == $type) {
  420. $mapping[$type]['selector'] = str_replace(
  421. '%option_name%',
  422. $mapping[$type]['value'],
  423. $mapping[$type]['selector']
  424. );
  425. $mapping[$type]['value'] = 'Yes';
  426. }
  427. $this->_fill($mapping, $optionBlock);
  428. }
  429. }
  430. /**
  431. * Get customer option type
  432. *
  433. * @param string $option
  434. * @return string
  435. */
  436. protected function getOptionType($option)
  437. {
  438. $option = strpos($option, "/") !== false ? substr($option, strpos($option, "/") + 1) : $option;
  439. return strtolower(preg_replace('/[^a-z]/i', '', $option));
  440. }
  441. }