Completed
Push — 1.5 ( 3e98ab...a62d68 )
by Kamil
15:55 queued 08:45
created

CompletePage::getValidationErrors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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\Session;
19
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage;
20
use Sylius\Behat\Service\Accessor\TableAccessorInterface;
21
use Sylius\Component\Core\Model\AddressInterface;
22
use Sylius\Component\Core\Model\ProductInterface;
23
use Sylius\Component\Core\Model\ShippingMethodInterface;
24
use Symfony\Component\Intl\Intl;
25
use Symfony\Component\Routing\RouterInterface;
26
27
class CompletePage extends SymfonyPage implements CompletePageInterface
28
{
29
    /** @var TableAccessorInterface */
30
    private $tableAccessor;
31
32
    public function __construct(
33
        Session $session,
34
        $minkParameters,
35
        RouterInterface $router,
36
        TableAccessorInterface $tableAccessor
37
    ) {
38
        parent::__construct($session, $minkParameters, $router);
39
40
        $this->tableAccessor = $tableAccessor;
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46
    public function getRouteName(): string
47
    {
48
        return 'sylius_shop_checkout_complete';
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function hasItemWithProductAndQuantity($productName, $quantity)
55
    {
56
        $table = $this->getElement('items_table');
57
58
        try {
59
            $this->tableAccessor->getRowWithFields($table, ['item' => $productName, 'qty' => $quantity]);
60
        } catch (\InvalidArgumentException $exception) {
61
            return false;
62
        }
63
64
        return true;
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function hasShippingAddress(AddressInterface $address)
71
    {
72
        $shippingAddress = $this->getElement('shipping_address')->getText();
73
74
        return $this->isAddressValid($shippingAddress, $address);
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function hasBillingAddress(AddressInterface $address)
81
    {
82
        $billingAddress = $this->getElement('billing_address')->getText();
83
84
        return $this->isAddressValid($billingAddress, $address);
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function hasShippingMethod(ShippingMethodInterface $shippingMethod)
91
    {
92
        if (!$this->hasElement('shipping_method')) {
93
            return false;
94
        }
95
96
        return false !== strpos($this->getElement('shipping_method')->getText(), $shippingMethod->getName());
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function getPaymentMethodName()
103
    {
104
        return $this->getElement('payment_method')->getText();
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function hasPaymentMethod()
111
    {
112
        return $this->hasElement('payment_method');
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function hasProductDiscountedUnitPriceBy(ProductInterface $product, $amount)
119
    {
120
        $columns = $this->getProductRowElement($product)->findAll('css', 'td');
121
        $priceWithoutDiscount = $this->getPriceFromString($columns[1]->getText());
122
        $priceWithDiscount = $this->getPriceFromString($columns[3]->getText());
123
        $discount = $priceWithoutDiscount - $priceWithDiscount;
124
125
        return $discount === $amount;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    public function hasOrderTotal($total)
132
    {
133
        if (!$this->hasElement('order_total')) {
134
            return false;
135
        }
136
137
        return $this->getTotalFromString($this->getElement('order_total')->getText()) === $total;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function getBaseCurrencyOrderTotal()
144
    {
145
        return $this->getBaseTotalFromString($this->getElement('base_order_total')->getText());
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function addNotes($notes)
152
    {
153
        $this->getElement('extra_notes')->setValue($notes);
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function hasPromotionTotal($promotionTotal)
160
    {
161
        return false !== strpos($this->getElement('promotion_total')->getText(), $promotionTotal);
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function hasPromotion($promotionName)
168
    {
169
        return false !== stripos($this->getElement('promotion_discounts')->getText(), $promotionName);
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function hasShippingPromotion($promotionName)
176
    {
177
        return false !== stripos($this->getElement('promotion_shipping_discounts')->getText(), $promotionName);
178
    }
179
180
    public function getTaxTotal(): string
181
    {
182
        return str_replace('Tax total: ', '', $this->getElement('tax_total')->getText());
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function hasShippingTotal($price)
189
    {
190
        return false !== strpos($this->getElement('shipping_total')->getText(), $price);
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function hasProductUnitPrice(ProductInterface $product, $price)
197
    {
198
        $productRowElement = $this->getProductRowElement($product);
199
200
        return null !== $productRowElement->find('css', sprintf('td:contains("%s")', $price));
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function hasProductOutOfStockValidationMessage(ProductInterface $product)
207
    {
208
        $message = sprintf('%s does not have sufficient stock.', $product->getName());
209
210
        return $this->getElement('validation_errors')->getText() === $message;
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function getValidationErrors()
217
    {
218
        return $this->getElement('validation_errors')->getText();
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224
    public function hasLocale($localeName)
225
    {
226
        return false !== strpos($this->getElement('locale')->getText(), $localeName);
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232
    public function hasCurrency($currencyCode)
233
    {
234
        return false !== strpos($this->getElement('currency')->getText(), $currencyCode);
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function confirmOrder()
241
    {
242
        $this->getElement('confirm_button')->press();
243
    }
244
245
    public function changeAddress()
246
    {
247
        $this->getElement('addressing_step_label')->click();
248
    }
249
250
    public function changeShippingMethod()
251
    {
252
        $this->getElement('shipping_step_label')->click();
253
    }
254
255
    public function changePaymentMethod()
256
    {
257
        $this->getElement('payment_step_label')->click();
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263
    public function hasShippingProvinceName($provinceName)
264
    {
265
        $shippingAddressText = $this->getElement('shipping_address')->getText();
266
267
        return false !== stripos($shippingAddressText, $provinceName);
268
    }
269
270
    /**
271
     * {@inheritdoc}
272
     */
273
    public function hasBillingProvinceName($provinceName)
274
    {
275
        $billingAddressText = $this->getElement('billing_address')->getText();
276
277
        return false !== stripos($billingAddressText, $provinceName);
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283
    public function getShippingPromotionDiscount($promotionName)
284
    {
285
        return $this->getElement('promotion_shipping_discounts')->find('css', '.description')->getText();
286
    }
287
288
    /**
289
     * {@inheritdoc}
290
     */
291
    public function tryToOpen(array $urlParameters = []): void
292
    {
293
        if ($this->getDriver() instanceof Selenium2Driver) {
294
            $start = microtime(true);
295
            $end = $start + 15;
296
            do {
297
                parent::tryToOpen($urlParameters);
298
                sleep(3);
299
            } while (!$this->isOpen() && microtime(true) < $end);
300
301
            return;
302
        }
303
304
        parent::tryToOpen($urlParameters);
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    protected function getDefinedElements(): array
311
    {
312
        return array_merge(parent::getDefinedElements(), [
313
            'addressing_step_label' => '.steps a:contains("Address")',
314
            'base_order_total' => '#base-total',
315
            'billing_address' => '#sylius-billing-address',
316
            'confirm_button' => 'form button',
317
            'currency' => '#sylius-order-currency-code',
318
            'extra_notes' => '#sylius_checkout_complete_notes',
319
            'items_table' => '#sylius-order',
320
            'locale' => '#sylius-order-locale-name',
321
            'order_total' => 'td:contains("Total")',
322
            'payment_method' => '#sylius-payment-method',
323
            'payment_step_label' => '.steps a:contains("Payment")',
324
            'product_row' => 'tbody tr:contains("%name%")',
325
            'promotion_discounts' => '#promotion-discounts',
326
            'promotion_shipping_discounts' => '#promotion-shipping-discounts',
327
            'promotion_total' => '#promotion-total',
328
            'shipping_address' => '#sylius-shipping-address',
329
            'shipping_method' => '#sylius-shipping-method',
330
            'shipping_step_label' => '.steps a:contains("Shipping")',
331
            'shipping_total' => '#shipping-total',
332
            'tax_total' => '#tax-total',
333
            'validation_errors' => '.sylius-validation-error',
334
        ]);
335
    }
336
337
    /**
338
     * @return NodeElement
339
     */
340
    private function getProductRowElement(ProductInterface $product)
341
    {
342
        return $this->getElement('product_row', ['%name%' => $product->getName()]);
343
    }
344
345
    /**
346
     * @param string $displayedAddress
347
     *
348
     * @return bool
349
     */
350
    private function isAddressValid($displayedAddress, AddressInterface $address)
351
    {
352
        return
353
            $this->hasAddressPart($displayedAddress, $address->getCompany(), true) &&
354
            $this->hasAddressPart($displayedAddress, $address->getFirstName()) &&
355
            $this->hasAddressPart($displayedAddress, $address->getLastName()) &&
356
            $this->hasAddressPart($displayedAddress, $address->getPhoneNumber(), true) &&
357
            $this->hasAddressPart($displayedAddress, $address->getStreet()) &&
358
            $this->hasAddressPart($displayedAddress, $address->getCity()) &&
359
            $this->hasAddressPart($displayedAddress, $address->getProvinceCode(), true) &&
360
            $this->hasAddressPart($displayedAddress, $this->getCountryName($address->getCountryCode())) &&
361
            $this->hasAddressPart($displayedAddress, $address->getPostcode())
362
        ;
363
    }
364
365
    /**
366
     * @param string $address
367
     * @param string $addressPart
368
     *
369
     * @return bool
370
     */
371
    private function hasAddressPart($address, $addressPart, $optional = false)
372
    {
373
        if ($optional && null === $addressPart) {
374
            return true;
375
        }
376
377
        return false !== strpos($address, $addressPart);
378
    }
379
380
    /**
381
     * @param string $countryCode
382
     *
383
     * @return string
384
     */
385
    private function getCountryName($countryCode)
386
    {
387
        return strtoupper(Intl::getRegionBundle()->getCountryName($countryCode, 'en'));
388
    }
389
390
    private function getPriceFromString(string $price): int
391
    {
392
        return (int) round((float) str_replace(['€', '£', '$'], '', $price) * 100, 2);
393
    }
394
395
    /**
396
     * @param string $total
397
     *
398
     * @return int
399
     */
400
    private function getTotalFromString($total)
401
    {
402
        $total = str_replace('Total:', '', $total);
403
404
        return $this->getPriceFromString($total);
405
    }
406
407
    /**
408
     * @param string $total
409
     *
410
     * @return int
411
     */
412
    private function getBaseTotalFromString($total)
413
    {
414
        $total = str_replace('Total in base currency:', '', $total);
415
416
        return $this->getPriceFromString($total);
417
    }
418
}
419