Completed
Push — 1.7 ( f471ab...663834 )
by Kamil
04:59
created

CompletePage::isAddressValid()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 8.0555
c 0
b 0
f 0
cc 9
nc 9
nop 2
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
    public function getRouteName(): string
44
    {
45
        return 'sylius_shop_checkout_complete';
46
    }
47
48
    public function hasItemWithProductAndQuantity(string $productName, string $quantity): bool
49
    {
50
        $table = $this->getElement('items_table');
51
52
        try {
53
            $this->tableAccessor->getRowWithFields($table, ['item' => $productName, 'qty' => $quantity]);
54
        } catch (\InvalidArgumentException $exception) {
55
            return false;
56
        }
57
58
        return true;
59
    }
60
61
    public function hasShippingAddress(AddressInterface $address): bool
62
    {
63
        $shippingAddress = $this->getElement('shipping_address')->getText();
64
65
        return $this->isAddressValid($shippingAddress, $address);
66
    }
67
68
    public function hasBillingAddress(AddressInterface $address): bool
69
    {
70
        $billingAddress = $this->getElement('billing_address')->getText();
71
72
        return $this->isAddressValid($billingAddress, $address);
73
    }
74
75
    public function hasShippingMethod(ShippingMethodInterface $shippingMethod): bool
76
    {
77
        if (!$this->hasElement('shipping_method')) {
78
            return false;
79
        }
80
81
        return false !== strpos($this->getElement('shipping_method')->getText(), $shippingMethod->getName());
82
    }
83
84
    public function getPaymentMethodName(): string
85
    {
86
        return $this->getElement('payment_method')->getText();
87
    }
88
89
    public function hasPaymentMethod(): bool
90
    {
91
        return $this->hasElement('payment_method');
92
    }
93
94
    public function hasProductDiscountedUnitPriceBy(ProductInterface $product, int $amount): bool
95
    {
96
        $priceWithoutDiscount = $this->getPriceFromString($this->getElement('product_old_price', ['%name%' => $product->getName()])->getText());
97
        $priceWithDiscount = $this->getPriceFromString($this->getElement('product_unit_price', ['%name%' => $product->getName()])->getText());
98
        $discount = $priceWithoutDiscount - $priceWithDiscount;
99
100
        return $discount === $amount;
101
    }
102
103
    public function hasOrderTotal(int $total): bool
104
    {
105
        if (!$this->hasElement('order_total')) {
106
            return false;
107
        }
108
109
        return $this->getTotalFromString($this->getElement('order_total')->getText()) === $total;
110
    }
111
112
    public function getBaseCurrencyOrderTotal(): string
113
    {
114
        return (string) $this->getBaseTotalFromString($this->getElement('base_order_total')->getText());
115
    }
116
117
    public function addNotes(string $notes): void
118
    {
119
        $this->getElement('extra_notes')->setValue($notes);
120
    }
121
122
    public function hasPromotionTotal(string $promotionTotal): bool
123
    {
124
        return false !== strpos($this->getElement('promotion_total')->getText(), $promotionTotal);
125
    }
126
127
    public function hasPromotion(string $promotionName): bool
128
    {
129
        return false !== stripos($this->getElement('promotion_discounts')->getText(), $promotionName);
130
    }
131
132
    public function hasShippingPromotion(string $promotionName): bool
133
    {
134
        /** @var NodeElement $shippingPromotions */
135
        $shippingPromotions = $this->getElement('promotions_shipping_details');
136
137
        return false !== strpos($shippingPromotions->getAttribute('data-html'), $promotionName);
138
    }
139
140
    public function getTaxTotal(): string
141
    {
142
        return $this->getElement('tax_total')->getText();
143
    }
144
145
    public function getShippingTotal(): string
146
    {
147
        return $this->getElement('shipping_total')->getText();
148
    }
149
150
    public function hasShippingTotal(): bool
151
    {
152
        return $this->hasElement('shipping_total');
153
    }
154
155
    public function hasProductUnitPrice(ProductInterface $product, string $price): bool
156
    {
157
        return $this->getPriceFromString($this->getElement('product_unit_price', ['%name%' => $product->getName()])->getText()) === $this->getPriceFromString($price);
158
    }
159
160
    public function hasProductOutOfStockValidationMessage(ProductInterface $product): bool
161
    {
162
        $message = sprintf('%s does not have sufficient stock.', $product->getName());
163
164
        return $this->getElement('validation_errors')->getText() === $message;
165
    }
166
167
    public function getValidationErrors(): string
168
    {
169
        return $this->getElement('validation_errors')->getText();
170
    }
171
172
    public function hasLocale(string $localeName): bool
173
    {
174
        return false !== strpos($this->getElement('locale')->getText(), $localeName);
175
    }
176
177
    public function hasCurrency(string $currencyCode): bool
178
    {
179
        return false !== strpos($this->getElement('currency')->getText(), $currencyCode);
180
    }
181
182
    public function confirmOrder(): void
183
    {
184
        $this->getElement('confirm_button')->press();
185
    }
186
187
    public function changeAddress(): void
188
    {
189
        $this->getElement('addressing_step_label')->click();
190
    }
191
192
    public function changeShippingMethod(): void
193
    {
194
        $this->getElement('shipping_step_label')->click();
195
    }
196
197
    public function changePaymentMethod(): void
198
    {
199
        $this->getElement('payment_step_label')->click();
200
    }
201
202
    public function hasShippingProvinceName(string $provinceName): bool
203
    {
204
        $shippingAddressText = $this->getElement('shipping_address')->getText();
205
206
        return false !== stripos($shippingAddressText, $provinceName);
207
    }
208
209
    public function hasBillingProvinceName(string $provinceName): bool
210
    {
211
        $billingAddressText = $this->getElement('billing_address')->getText();
212
213
        return false !== stripos($billingAddressText, $provinceName);
214
    }
215
216
    public function hasShippingPromotionWithDiscount(string $promotionName, string $discount): bool
217
    {
218
        $promotionWithDiscount = sprintf('%s: %s', $promotionName, $discount);
219
220
        /** @var NodeElement $shippingPromotions */
221
        $shippingPromotions = $this->getElement('promotions_shipping_details');
222
223
        return false !== strpos($shippingPromotions->getAttribute('data-html'), $promotionWithDiscount);
224
    }
225
226
    public function hasOrderPromotion(string $promotionName): bool
227
    {
228
        /** @var NodeElement $shippingPromotions */
229
        $shippingPromotions = $this->getElement('order_promotions_details');
230
231
        return false !== strpos($shippingPromotions->getAttribute('data-html'), $promotionName);
232
    }
233
234
    public function tryToOpen(array $urlParameters = []): void
235
    {
236
        if ($this->getDriver() instanceof Selenium2Driver) {
237
            $start = microtime(true);
238
            $end = $start + 15;
239
            do {
240
                parent::tryToOpen($urlParameters);
241
                sleep(3);
242
            } while (!$this->isOpen() && microtime(true) < $end);
243
244
            return;
245
        }
246
247
        parent::tryToOpen($urlParameters);
248
    }
249
250
    protected function getDefinedElements(): array
251
    {
252
        return array_merge(parent::getDefinedElements(), [
253
            'addressing_step_label' => '[data-test-step-address]',
254
            'base_order_total' => '[data-test-summary-order-total]',
255
            'billing_address' => '[data-test-billing-address]',
256
            'confirm_button' => '[data-test-confirmation-button]',
257
            'currency' => '[data-test-order-currency-code]',
258
            'extra_notes' => '[data-test-extra-notes]',
259
            'items_table' => '[data-test-order-table]',
260
            'locale' => '[data-test-order-locale-name]',
261
            'order_promotions_details' => '[data-test-order-promotions-details]',
262
            'order_total' => '[data-test-order-total]',
263
            'payment_method' => '[data-test-payment-method]',
264
            'payment_step_label' => '[data-test-step-payment]',
265
            'product_old_price' => '[data-test-product-old-price="%name%"]',
266
            'product_row' => '[data-test-product-row="%name%"]',
267
            'product_unit_price' => '[data-test-product-unit-price="%name%"]',
268
            'promotion_discounts' => '[data-test-promotion-discounts]',
269
            'promotion_shipping_discounts' => '[data-test-promotion-shipping-discounts]',
270
            'promotion_total' => '[data-test-promotion-total]',
271
            'promotions_shipping_details' => '[data-test-shipping-promotion-details]',
272
            'shipping_address' => '[data-test-shipping-address]',
273
            'shipping_method' => '[data-test-shipping-method]',
274
            'shipping_step_label' => '[data-test-step-shipping]',
275
            'shipping_total' => '[data-test-shipping-total]',
276
            'tax_total' => '[data-test-tax-total]',
277
            'validation_errors' => '[data-test-validation-error]',
278
        ]);
279
    }
280
281
    private function isAddressValid(string $displayedAddress, AddressInterface $address): bool
282
    {
283
        return
284
            $this->hasAddressPart($displayedAddress, $address->getCompany(), true) &&
285
            $this->hasAddressPart($displayedAddress, $address->getFirstName()) &&
286
            $this->hasAddressPart($displayedAddress, $address->getLastName()) &&
287
            $this->hasAddressPart($displayedAddress, $address->getPhoneNumber(), true) &&
288
            $this->hasAddressPart($displayedAddress, $address->getStreet()) &&
289
            $this->hasAddressPart($displayedAddress, $address->getCity()) &&
290
            $this->hasAddressPart($displayedAddress, $address->getProvinceCode(), true) &&
291
            $this->hasAddressPart($displayedAddress, $this->getCountryName($address->getCountryCode())) &&
292
            $this->hasAddressPart($displayedAddress, $address->getPostcode())
293
        ;
294
    }
295
296
    private function hasAddressPart(string $address, ?string $addressPart, bool $optional = false): bool
297
    {
298
        if ($optional && null === $addressPart) {
299
            return true;
300
        }
301
302
        return false !== strpos($address, $addressPart);
303
    }
304
305
    private function getCountryName(string $countryCode): string
306
    {
307
        return strtoupper(Intl::getRegionBundle()->getCountryName($countryCode, 'en'));
308
    }
309
310
    private function getPriceFromString(string $price): int
311
    {
312
        return (int) round((float) str_replace(['€', '£', '$'], '', $price) * 100, 2);
313
    }
314
315
    private function getTotalFromString(string $total): int
316
    {
317
        $total = str_replace('Total:', '', $total);
318
319
        return $this->getPriceFromString($total);
320
    }
321
322
    private function getBaseTotalFromString(string $total): int
323
    {
324
        $total = str_replace('Total in base currency:', '', $total);
325
326
        return $this->getPriceFromString($total);
327
    }
328
}
329