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\Checkout; |
15
|
|
|
|
16
|
|
|
use Behat\Mink\Driver\Selenium2Driver; |
17
|
|
|
use Behat\Mink\Element\NodeElement; |
18
|
|
|
use Behat\Mink\Exception\ElementNotFoundException; |
19
|
|
|
use Behat\Mink\Session; |
20
|
|
|
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage; |
21
|
|
|
use Sylius\Behat\Service\JQueryHelper; |
22
|
|
|
use Sylius\Component\Core\Factory\AddressFactoryInterface; |
23
|
|
|
use Sylius\Component\Core\Model\AddressInterface; |
24
|
|
|
use Symfony\Component\Routing\RouterInterface; |
25
|
|
|
use Webmozart\Assert\Assert; |
26
|
|
|
|
27
|
|
|
class AddressPage extends SymfonyPage implements AddressPageInterface |
28
|
|
|
{ |
29
|
|
|
public const TYPE_BILLING = 'billing'; |
30
|
|
|
|
31
|
|
|
public const TYPE_SHIPPING = 'shipping'; |
32
|
|
|
|
33
|
|
|
/** @var AddressFactoryInterface */ |
34
|
|
|
private $addressFactory; |
35
|
|
|
|
36
|
|
|
public function __construct( |
37
|
|
|
Session $session, |
38
|
|
|
$minkParameters, |
39
|
|
|
RouterInterface $router, |
40
|
|
|
AddressFactoryInterface $addressFactory |
41
|
|
|
) { |
42
|
|
|
parent::__construct($session, $minkParameters, $router); |
43
|
|
|
|
44
|
|
|
$this->addressFactory = $addressFactory; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
public function getRouteName(): string |
48
|
|
|
{ |
49
|
|
|
return 'sylius_shop_checkout_address'; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
public function chooseDifferentShippingAddress(): void |
53
|
|
|
{ |
54
|
|
|
$driver = $this->getDriver(); |
55
|
|
|
if ($driver instanceof Selenium2Driver) { |
56
|
|
|
$this->getElement('different_shipping_address_label')->click(); |
57
|
|
|
|
58
|
|
|
return; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
$billingAddressSwitch = $this->getElement('different_shipping_address'); |
62
|
|
|
Assert::false( |
63
|
|
|
$billingAddressSwitch->isChecked(), |
64
|
|
|
'Previous state of different billing address switch was true expected to be false' |
65
|
|
|
); |
66
|
|
|
|
67
|
|
|
$billingAddressSwitch->check(); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
public function checkInvalidCredentialsValidation(): bool |
71
|
|
|
{ |
72
|
|
|
$this->getElement('login_password')->waitFor(5, function () { |
73
|
|
|
$validationElement = $this->getElement('login_password')->getParent()->find('css', '[data-test-login-errors]'); |
74
|
|
|
if (null === $validationElement) { |
75
|
|
|
return false; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
return $validationElement->isVisible(); |
79
|
|
|
}); |
80
|
|
|
|
81
|
|
|
return $this->checkValidationMessageFor('login_password', 'Invalid credentials.'); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
public function checkValidationMessageFor(string $element, string $message): bool |
85
|
|
|
{ |
86
|
|
|
$foundElement = $this->getFieldElement($element); |
87
|
|
|
if (null === $foundElement) { |
88
|
|
|
throw new ElementNotFoundException($this->getSession(), 'Validation message', 'css', '[data-test-validation-error]'); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
$validationMessage = $foundElement->find('css', '.sylius-validation-error'); |
92
|
|
|
if (null === $validationMessage) { |
93
|
|
|
throw new ElementNotFoundException($this->getSession(), 'Validation message', 'css', '[data-test-validation-error]'); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
return $message === $validationMessage->getText(); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
public function specifyShippingAddress(AddressInterface $shippingAddress): void |
100
|
|
|
{ |
101
|
|
|
$this->specifyAddress($shippingAddress, self::TYPE_SHIPPING); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
public function selectShippingAddressProvince(string $province): void |
105
|
|
|
{ |
106
|
|
|
$this->waitForElement(5, 'shipping_country_province'); |
107
|
|
|
$this->getElement('shipping_country_province')->selectOption($province); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
public function specifyBillingAddress(AddressInterface $billingAddress): void |
111
|
|
|
{ |
112
|
|
|
$this->specifyAddress($billingAddress, self::TYPE_BILLING); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
public function selectBillingAddressProvince(string $province): void |
116
|
|
|
{ |
117
|
|
|
$this->waitForElement(5, 'billing_country_province'); |
118
|
|
|
$this->getElement('billing_country_province')->selectOption($province); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
public function specifyEmail(?string $email): void |
122
|
|
|
{ |
123
|
|
|
$this->getElement('customer_email')->setValue($email); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
public function specifyBillingAddressFullName(string $fullName): void |
127
|
|
|
{ |
128
|
|
|
$names = explode(' ', $fullName); |
129
|
|
|
|
130
|
|
|
$this->getElement('billing_first_name')->setValue($names[0]); |
131
|
|
|
$this->getElement('billing_last_name')->setValue($names[1]); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
public function canSignIn(): bool |
135
|
|
|
{ |
136
|
|
|
return $this->waitForElement(5, 'login_button'); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
public function signIn(): void |
140
|
|
|
{ |
141
|
|
|
$this->waitForElement(5, 'login_button'); |
142
|
|
|
|
143
|
|
|
try { |
144
|
|
|
$this->getElement('login_button')->press(); |
145
|
|
|
} catch (ElementNotFoundException $elementNotFoundException) { |
146
|
|
|
$this->getElement('login_button')->click(); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$this->waitForLoginAction(); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
public function specifyPassword(string $password): void |
153
|
|
|
{ |
154
|
|
|
$this->getDocument()->waitFor(5, function () { |
155
|
|
|
return $this->getElement('login_password')->isVisible(); |
156
|
|
|
}); |
157
|
|
|
|
158
|
|
|
$this->getElement('login_password')->setValue($password); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
public function getItemSubtotal(string $itemName): string |
162
|
|
|
{ |
163
|
|
|
$itemSlug = strtolower(str_replace('\"', '', str_replace(' ', '-', $itemName))); |
164
|
|
|
|
165
|
|
|
$subtotalTable = $this->getElement('checkout_subtotal'); |
166
|
|
|
|
167
|
|
|
return $subtotalTable->find('css', sprintf('[data-test-item-subtotal="%s"]', $itemSlug))->getText(); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
public function getShippingAddressCountry(): string |
171
|
|
|
{ |
172
|
|
|
return $this->getElement('shipping_country')->find('css', 'option:selected')->getText(); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
public function nextStep(): void |
176
|
|
|
{ |
177
|
|
|
$this->getElement('next_step')->press(); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
public function backToStore(): void |
181
|
|
|
{ |
182
|
|
|
$this->getDocument()->clickLink('Back to store'); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
public function specifyBillingAddressProvince(string $provinceName): void |
186
|
|
|
{ |
187
|
|
|
$this->waitForElement(5, 'billing_province'); |
188
|
|
|
$this->getElement('billing_province')->setValue($provinceName); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
public function specifyShippingAddressProvince(string $provinceName): void |
192
|
|
|
{ |
193
|
|
|
$this->waitForElement(5, 'shipping_province'); |
194
|
|
|
$this->getElement('shipping_province')->setValue($provinceName); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
public function hasShippingAddressInput(): bool |
198
|
|
|
{ |
199
|
|
|
return $this->waitForElement(5, 'shipping_province'); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
public function hasBillingAddressInput(): bool |
203
|
|
|
{ |
204
|
|
|
return $this->waitForElement(5, 'billing_province'); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
public function selectShippingAddressFromAddressBook(AddressInterface $address): void |
208
|
|
|
{ |
209
|
|
|
$this->waitForElement(2, sprintf('%s_province', self::TYPE_SHIPPING)); |
210
|
|
|
$addressBookSelect = $this->getElement('shipping_address_book'); |
211
|
|
|
|
212
|
|
|
$addressBookSelect->click(); |
213
|
|
|
$addressOption = $addressBookSelect->waitFor(5, function () use ($address, $addressBookSelect) { |
214
|
|
|
return $addressBookSelect->find('css', sprintf('.item[data-id="%s"]', $address->getId())); |
215
|
|
|
}); |
216
|
|
|
|
217
|
|
|
if (null === $addressOption) { |
218
|
|
|
throw new ElementNotFoundException($this->getDriver(), 'option', 'css', sprintf('.item[data-id="%s"]', $address->getId())); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
$addressOption->click(); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
public function selectBillingAddressFromAddressBook(AddressInterface $address): void |
225
|
|
|
{ |
226
|
|
|
$this->waitForElement(2, sprintf('%s_province', self::TYPE_BILLING)); |
227
|
|
|
$addressBookSelect = $this->getElement('billing_address_book'); |
228
|
|
|
|
229
|
|
|
$addressBookSelect->click(); |
230
|
|
|
$addressOption = $addressBookSelect->waitFor(5, function () use ($address, $addressBookSelect) { |
231
|
|
|
return $addressBookSelect->find('css', sprintf('.item[data-id="%s"]', $address->getId())); |
232
|
|
|
}); |
233
|
|
|
|
234
|
|
|
if (null === $addressOption) { |
235
|
|
|
throw new ElementNotFoundException($this->getDriver(), 'option', 'css', sprintf('.item[data-id="%s"]', $address->getId())); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
$addressOption->click(); |
239
|
|
|
|
240
|
|
|
JQueryHelper::waitForFormToStopLoading($this->getDocument()); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
public function getPreFilledShippingAddress(): AddressInterface |
244
|
|
|
{ |
245
|
|
|
return $this->getPreFilledAddress(self::TYPE_SHIPPING); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
public function getPreFilledBillingAddress(): AddressInterface |
249
|
|
|
{ |
250
|
|
|
return $this->getPreFilledAddress(self::TYPE_BILLING); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
protected function getDefinedElements(): array |
254
|
|
|
{ |
255
|
|
|
return array_merge(parent::getDefinedElements(), [ |
256
|
|
|
'billing_address_book' => '[data-test-billing-address] [data-test-address-book]', |
257
|
|
|
'billing_city' => '[data-test-billing-city]', |
258
|
|
|
'billing_country' => '[data-test-billing-country]', |
259
|
|
|
'billing_country_province' => '[data-test-billing-address] [data-test-province-code]', |
260
|
|
|
'billing_first_name' => '[data-test-billing-first-name]', |
261
|
|
|
'billing_last_name' => '[data-test-billing-last-name]', |
262
|
|
|
'billing_postcode' => '[data-test-billing-postcode]', |
263
|
|
|
'billing_province' => '[data-test-billing-address] [data-test-province-name]', |
264
|
|
|
'billing_street' => '[data-test-billing-street]', |
265
|
|
|
'checkout_subtotal' => '[data-test-checkout-subtotal]', |
266
|
|
|
'customer_email' => '[data-test-login-email]', |
267
|
|
|
'different_billing_address' => '[data-test-different-billing-address]', |
268
|
|
|
'different_billing_address_label' => '[data-test-different-billing-address-label]', |
269
|
|
|
'different_shipping_address' => '[data-test-different-shipping-address]', |
270
|
|
|
'different_shipping_address_label' => '[data-test-different-shipping-address-label]', |
271
|
|
|
'login_button' => '[data-test-login-button]', |
272
|
|
|
'login_password' => '[data-test-password-input]', |
273
|
|
|
'next_step' => '[data-test-next-step]', |
274
|
|
|
'shipping_address_book' => '[data-test-shipping-address] [data-test-address-book]', |
275
|
|
|
'shipping_city' => '[data-test-shipping-city]', |
276
|
|
|
'shipping_country' => '[data-test-shipping-country]', |
277
|
|
|
'shipping_country_province' => '[data-test-shipping-address] [data-test-province-code]', |
278
|
|
|
'shipping_first_name' => '[data-test-shipping-first-name]', |
279
|
|
|
'shipping_last_name' => '[data-test-shipping-last-name]', |
280
|
|
|
'shipping_postcode' => '[data-test-shipping-postcode]', |
281
|
|
|
'shipping_province' => '[data-test-shipping-address] [data-test-province-name]', |
282
|
|
|
'shipping_street' => '[data-test-shipping-street]', |
283
|
|
|
]); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
private function getPreFilledAddress(string $type): AddressInterface |
287
|
|
|
{ |
288
|
|
|
$this->assertAddressType($type); |
289
|
|
|
|
290
|
|
|
/** @var AddressInterface $address */ |
291
|
|
|
$address = $this->addressFactory->createNew(); |
292
|
|
|
|
293
|
|
|
$address->setFirstName($this->getElement(sprintf('%s_first_name', $type))->getValue()); |
294
|
|
|
$address->setLastName($this->getElement(sprintf('%s_last_name', $type))->getValue()); |
295
|
|
|
$address->setStreet($this->getElement(sprintf('%s_street', $type))->getValue()); |
296
|
|
|
$address->setCountryCode($this->getElement(sprintf('%s_country', $type))->getValue()); |
297
|
|
|
$address->setCity($this->getElement(sprintf('%s_city', $type))->getValue()); |
298
|
|
|
$address->setPostcode($this->getElement(sprintf('%s_postcode', $type))->getValue()); |
299
|
|
|
$this->waitForElement(5, sprintf('%s_province', $type)); |
300
|
|
|
|
301
|
|
|
try { |
302
|
|
|
$address->setProvinceName($this->getElement(sprintf('%s_province', $type))->getValue()); |
303
|
|
|
} catch (ElementNotFoundException $exception) { |
304
|
|
|
$address->setProvinceCode($this->getElement(sprintf('%s_country_province', $type))->getValue()); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
return $address; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
private function specifyAddress(AddressInterface $address, string $type): void |
311
|
|
|
{ |
312
|
|
|
$this->assertAddressType($type); |
313
|
|
|
|
314
|
|
|
$this->getElement(sprintf('%s_first_name', $type))->setValue($address->getFirstName()); |
315
|
|
|
$this->getElement(sprintf('%s_last_name', $type))->setValue($address->getLastName()); |
316
|
|
|
$this->getElement(sprintf('%s_street', $type))->setValue($address->getStreet()); |
317
|
|
|
$this->getElement(sprintf('%s_country', $type))->selectOption($address->getCountryCode() ?: 'Select'); |
318
|
|
|
$this->getElement(sprintf('%s_city', $type))->setValue($address->getCity()); |
319
|
|
|
$this->getElement(sprintf('%s_postcode', $type))->setValue($address->getPostcode()); |
320
|
|
|
|
321
|
|
|
JQueryHelper::waitForFormToStopLoading($this->getDocument()); |
322
|
|
|
|
323
|
|
|
if (null !== $address->getProvinceName()) { |
324
|
|
|
$this->waitForElement(5, sprintf('%s_province', $type)); |
325
|
|
|
$this->getElement(sprintf('%s_province', $type))->setValue($address->getProvinceName()); |
326
|
|
|
} |
327
|
|
|
if (null !== $address->getProvinceCode()) { |
328
|
|
|
$this->waitForElement(5, sprintf('%s_country_province', $type)); |
329
|
|
|
$this->getElement(sprintf('%s_country_province', $type))->selectOption($address->getProvinceCode()); |
330
|
|
|
} |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
private function getFieldElement(string $element): ?NodeElement |
334
|
|
|
{ |
335
|
|
|
$element = $this->getElement($element); |
336
|
|
|
while (null !== $element && !$element->hasClass('field')) { |
337
|
|
|
$element = $element->getParent(); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
return $element; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
private function waitForLoginAction(): bool |
|
|
|
|
344
|
|
|
{ |
345
|
|
|
return $this->getDocument()->waitFor(5, function () { |
346
|
|
|
return !$this->hasElement('login_password'); |
347
|
|
|
}); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
private function waitForElement(int $timeout, string $elementName): bool |
|
|
|
|
351
|
|
|
{ |
352
|
|
|
return $this->getDocument()->waitFor($timeout, function () use ($elementName) { |
353
|
|
|
return $this->hasElement($elementName); |
354
|
|
|
}); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
private function assertAddressType(string $type): void |
358
|
|
|
{ |
359
|
|
|
$availableTypes = [self::TYPE_BILLING, self::TYPE_SHIPPING]; |
360
|
|
|
|
361
|
|
|
Assert::oneOf($type, $availableTypes, sprintf('There are only two available types %s, %s. %s given', self::TYPE_BILLING, self::TYPE_SHIPPING, $type)); |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.