1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Sylius package. |
5
|
|
|
* |
6
|
|
|
* (c) Paweł Jędrzejewski |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
declare(strict_types=1); |
13
|
|
|
|
14
|
|
|
namespace Sylius\Behat\Page\Shop\Product; |
15
|
|
|
|
16
|
|
|
use Behat\Mink\Driver\Selenium2Driver; |
17
|
|
|
use Behat\Mink\Exception\ElementNotFoundException; |
18
|
|
|
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage; |
19
|
|
|
use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException; |
20
|
|
|
use Sylius\Behat\Service\JQueryHelper; |
21
|
|
|
use Sylius\Component\Product\Model\ProductInterface; |
22
|
|
|
use Sylius\Component\Product\Model\ProductOptionInterface; |
23
|
|
|
use Webmozart\Assert\Assert; |
24
|
|
|
|
25
|
|
|
class ShowPage extends SymfonyPage implements ShowPageInterface |
26
|
|
|
{ |
27
|
|
|
public function getRouteName(): string |
28
|
|
|
{ |
29
|
|
|
return 'sylius_shop_product_show'; |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
public function addToCart(): void |
33
|
|
|
{ |
34
|
|
|
$this->getElement('add_to_cart_button')->click(); |
35
|
|
|
|
36
|
|
|
if ($this->getDriver() instanceof Selenium2Driver) { |
37
|
|
|
JQueryHelper::waitForAsynchronousActionsToFinish($this->getSession()); |
38
|
|
|
} |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
public function addToCartWithQuantity(string $quantity): void |
42
|
|
|
{ |
43
|
|
|
$this->getElement('quantity')->setValue($quantity); |
44
|
|
|
$this->getElement('add_to_cart_button')->click(); |
45
|
|
|
|
46
|
|
|
if ($this->getDriver() instanceof Selenium2Driver) { |
47
|
|
|
JQueryHelper::waitForAsynchronousActionsToFinish($this->getSession()); |
48
|
|
|
} |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
public function addToCartWithVariant(string $variant): void |
52
|
|
|
{ |
53
|
|
|
$this->selectVariant($variant); |
54
|
|
|
|
55
|
|
|
$this->getElement('add_to_cart_button')->click(); |
56
|
|
|
|
57
|
|
|
if ($this->getDriver() instanceof Selenium2Driver) { |
58
|
|
|
JQueryHelper::waitForAsynchronousActionsToFinish($this->getSession()); |
59
|
|
|
} |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
public function addToCartWithOption(ProductOptionInterface $option, string $optionValue): void |
63
|
|
|
{ |
64
|
|
|
$select = $this->getElement('option_select', ['%optionCode%' => $option->getCode()]); |
65
|
|
|
|
66
|
|
|
$this->getDocument()->selectFieldOption($select->getAttribute('name'), $optionValue); |
67
|
|
|
$this->getElement('add_to_cart_button')->click(); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
public function getAttributeByName(string $name): ?string |
71
|
|
|
{ |
72
|
|
|
$attributesTable = $this->getElement('attributes'); |
73
|
|
|
|
74
|
|
|
$driver = $this->getDriver(); |
75
|
|
|
if ($driver instanceof Selenium2Driver) { |
76
|
|
|
try { |
77
|
|
|
$attributesTab = $this->getElement('tab', ['%name%' => 'attributes']); |
78
|
|
|
if (!$attributesTab->hasAttribute('[data-test-active]')) { |
79
|
|
|
$attributesTab->click(); |
80
|
|
|
} |
81
|
|
|
} catch (ElementNotFoundException $exception) { |
|
|
|
|
82
|
|
|
return null; |
83
|
|
|
} |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$nameTd = $attributesTable->find('css', sprintf('[data-test-product-attribute-name="%s"]', $name)); |
87
|
|
|
|
88
|
|
|
if (null === $nameTd) { |
89
|
|
|
return null; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
$row = $nameTd->getParent(); |
93
|
|
|
|
94
|
|
|
return trim($row->find('css', '[data-test-product-attribute-value]')->getText()); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
public function getAttributes(): array |
98
|
|
|
{ |
99
|
|
|
$attributesTable = $this->getElement('attributes'); |
100
|
|
|
|
101
|
|
|
return $attributesTable->findAll('css', '[data-test-product-attribute-name]'); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
public function getAverageRating(): float |
105
|
|
|
{ |
106
|
|
|
return (float) $this->getElement('average_rating')->getAttribute('data-test-average-rating'); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
public function getCurrentUrl(): string |
110
|
|
|
{ |
111
|
|
|
return $this->getDriver()->getCurrentUrl(); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
public function getCurrentVariantName(): string |
115
|
|
|
{ |
116
|
|
|
$currentVariantRow = $this->getElement('current_variant_input')->getParent()->getParent(); |
117
|
|
|
|
118
|
|
|
return $currentVariantRow->find('css', 'td:first-child')->getText(); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
public function getName(): string |
122
|
|
|
{ |
123
|
|
|
return $this->getElement('product_name')->getText(); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
public function getPrice(): string |
127
|
|
|
{ |
128
|
|
|
return $this->getElement('product_price')->getText(); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
public function getOriginalPrice(): string |
132
|
|
|
{ |
133
|
|
|
return $this->getElement('product_original_price')->getText(); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
public function hasAddToCartButton(): bool |
137
|
|
|
{ |
138
|
|
|
if (!$this->hasElement('add_to_cart_button')) { |
139
|
|
|
return false; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
return $this->getElement('add_to_cart_button') !== null && false === $this->getElement('add_to_cart_button')->hasAttribute('disabled'); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function hasAssociation(string $productAssociationName): bool |
146
|
|
|
{ |
147
|
|
|
try { |
148
|
|
|
$this->getElement('association', ['%associationName%' => $productAssociationName]); |
149
|
|
|
} catch (ElementNotFoundException $e) { |
|
|
|
|
150
|
|
|
return false; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
return true; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
public function hasProductInAssociation(string $productName, string $productAssociationName): bool |
157
|
|
|
{ |
158
|
|
|
$products = $this->getElement('association', ['%associationName%' => $productAssociationName]); |
159
|
|
|
|
160
|
|
|
Assert::notNull($products); |
161
|
|
|
|
162
|
|
|
return $productName === $products->find('css', sprintf('[data-test-product-name="%s"]', $productName))->getText(); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
public function hasProductOutOfStockValidationMessage(ProductInterface $product): bool |
166
|
|
|
{ |
167
|
|
|
$message = sprintf('%s does not have sufficient stock.', $product->getName()); |
168
|
|
|
|
169
|
|
|
if (!$this->hasElement('validation_errors')) { |
170
|
|
|
return false; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
return $this->getElement('validation_errors')->getText() === $message; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
public function hasReviewTitled(string $title): bool |
177
|
|
|
{ |
178
|
|
|
try { |
179
|
|
|
$element = $this->getElement('reviews_comment', ['%title%' => $title]); |
180
|
|
|
} catch (ElementNotFoundException $e) { |
|
|
|
|
181
|
|
|
return false; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
return $title === $element->getAttribute('data-test-comment'); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
public function isOutOfStock(): bool |
188
|
|
|
{ |
189
|
|
|
return $this->hasElement('out_of_stock'); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
public function isMainImageDisplayed(): bool |
193
|
|
|
{ |
194
|
|
|
if (!$this->hasElement('main_image')) { |
195
|
|
|
return false; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
$imageUrl = $this->getElement('main_image')->getAttribute('src'); |
199
|
|
|
$this->getDriver()->visit($imageUrl); |
200
|
|
|
$pageText = $this->getDocument()->getText(); |
201
|
|
|
$this->getDriver()->back(); |
202
|
|
|
|
203
|
|
|
return false === stripos($pageText, '404 Not Found'); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
public function countReviews(): int |
207
|
|
|
{ |
208
|
|
|
return count($this->getElement('reviews')->findAll('css', '[data-test-comment]')); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
public function selectOption(string $optionCode, string $optionValue): void |
212
|
|
|
{ |
213
|
|
|
$optionElement = $this->getElement('option_select', ['%optionCode%' => strtoupper($optionCode)]); |
214
|
|
|
$optionElement->selectOption($optionValue); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
public function selectVariant(string $variantName): void |
218
|
|
|
{ |
219
|
|
|
$variantRadio = $this->getElement('variant_radio', ['%variantName%' => $variantName]); |
220
|
|
|
|
221
|
|
|
$driver = $this->getDriver(); |
222
|
|
|
if ($driver instanceof Selenium2Driver) { |
223
|
|
|
$variantRadio->click(); |
224
|
|
|
|
225
|
|
|
return; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
$this->getDocument()->fillField($variantRadio->getAttribute('name'), $variantRadio->getAttribute('value')); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
public function visit($url): void |
232
|
|
|
{ |
233
|
|
|
$absoluteUrl = $this->makePathAbsolute($url); |
234
|
|
|
$this->getDriver()->visit($absoluteUrl); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
public function open(array $urlParameters = []): void |
238
|
|
|
{ |
239
|
|
|
$start = microtime(true); |
240
|
|
|
$end = $start + 5; |
241
|
|
|
do { |
242
|
|
|
try { |
243
|
|
|
parent::open($urlParameters); |
244
|
|
|
$isOpen = true; |
245
|
|
|
} catch (UnexpectedPageException $exception) { |
|
|
|
|
246
|
|
|
$isOpen = false; |
247
|
|
|
sleep(1); |
248
|
|
|
} |
249
|
|
|
} while (!$isOpen && microtime(true) < $end); |
250
|
|
|
|
251
|
|
|
if (!$isOpen) { |
252
|
|
|
throw new UnexpectedPageException(); |
253
|
|
|
} |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
protected function getDefinedElements(): array |
257
|
|
|
{ |
258
|
|
|
return array_merge(parent::getDefinedElements(), [ |
259
|
|
|
'add_to_cart_button' => '[data-test-add-to-cart-button]', |
260
|
|
|
'association' => '[data-test-product-association="%associationName%"]', |
261
|
|
|
'attributes' => '[data-test-product-attributes]', |
262
|
|
|
'average_rating' => '[data-test-average-rating]', |
263
|
|
|
'current_variant_input' => '[data-test-product-variants] td input:checked', |
264
|
|
|
'main_image' => '[data-test-main-image]', |
265
|
|
|
'name' => '[data-test-product-name]', |
266
|
|
|
'option_select' => '#sylius_add_to_cart_cartItem_variant_%optionCode%', |
267
|
|
|
'out_of_stock' => '[data-test-product-out-of-stock]', |
268
|
|
|
'product_price' => '[data-test-product-price]', |
269
|
|
|
'product_original_price' => '[data-test-product-original-price]', |
270
|
|
|
'product_name' => '[data-test-product-name]', |
271
|
|
|
'reviews' => '[data-test-product-reviews]', |
272
|
|
|
'reviews_comment' => '[data-test-comment="%title%"]', |
273
|
|
|
'selecting_variants' => '[data-test-product-selecting-variant]', |
274
|
|
|
'tab' => '[data-test-tab="%name%"]', |
275
|
|
|
'quantity' => '[data-test-quantity]', |
276
|
|
|
'validation_errors' => '[data-test-cart-validation-error]', |
277
|
|
|
'variant_radio' => '[data-test-product-variants] tbody tr:contains("%variantName%") input', |
278
|
|
|
]); |
279
|
|
|
} |
280
|
|
|
} |
281
|
|
|
|
Scrutinizer analyzes your
composer.json
/composer.lock
file if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.