Completed
Push — master ( 49a1ab...66d152 )
by Paweł
19:14 queued 06:15
created

hasProductOutOfStockValidationMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
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
namespace Sylius\Behat\Page\Shop\Checkout;
13
14
use Behat\Mink\Session;
15
use Behat\Mink\Element\NodeElement;
16
use Sylius\Behat\Page\SymfonyPage;
17
use Sylius\Behat\Service\Accessor\TableAccessorInterface;
18
use Sylius\Component\Core\Model\AddressInterface;
19
use Sylius\Component\Core\Model\ShippingMethodInterface;
20
use Sylius\Component\Payment\Model\PaymentMethodInterface;
21
use Sylius\Component\Core\Model\ProductInterface;
22
use Symfony\Component\Intl\Intl;
23
use Symfony\Component\Routing\RouterInterface;
24
25
/**
26
 * @author Mateusz Zalewski <[email protected]>
27
 */
28
class CompletePage extends SymfonyPage implements CompletePageInterface
29
{
30
    /**
31
     * @var TableAccessorInterface
32
     */
33
    private $tableAccessor;
34
35
    /**
36
     * @param Session $session
37
     * @param array $parameters
38
     * @param RouterInterface $router
39
     * @param TableAccessorInterface $tableAccessor
40
     */
41
    public function __construct(
42
        Session $session,
43
        array $parameters,
44
        RouterInterface $router,
45
        TableAccessorInterface $tableAccessor
46
    ) {
47
        parent::__construct($session, $parameters, $router);
48
49
        $this->tableAccessor = $tableAccessor;
50
    }
51
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function getRouteName()
57
    {
58
        return 'sylius_shop_checkout_complete';
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function hasItemWithProductAndQuantity($productName, $quantity)
65
    {
66
        $table = $this->getElement('items_table');
67
68
        try {
69
            $this->tableAccessor->getRowWithFields($table, ['item' => $productName, 'qty' => $quantity]);
70
        } catch (\InvalidArgumentException $exception) {
71
            return false;
72
        }
73
74
        return true;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function hasShippingAddress(AddressInterface $address)
81
    {
82
        $shippingAddress = $this->getElement('shipping_address')->getText();
83
84
        return $this->isAddressValid($shippingAddress, $address);
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function hasBillingAddress(AddressInterface $address)
91
    {
92
        $billingAddress = $this->getElement('billing_address')->getText();
93
94
        return $this->isAddressValid($billingAddress, $address);
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100
    public function hasShippingMethod(ShippingMethodInterface $shippingMethod)
101
    {
102
        if (!$this->hasElement('shipping_method')) {
103
            return false;
104
        }
105
106
        return false !== strpos($this->getElement('shipping_method')->getText(), $shippingMethod->getName());
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function hasPaymentMethod(PaymentMethodInterface $paymentMethod)
113
    {
114
        if (!$this->hasElement('payment_method')) {
115
            return false;
116
        }
117
118
        return false !== strpos($this->getElement('payment_method')->getText(), $paymentMethod->getName());
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function hasProductDiscountedUnitPriceBy(ProductInterface $product, $amount)
125
    {
126
        $columns = $this->getProductRowElement($product)->findAll('css', 'td');
127
        $priceWithoutDiscount = $this->getPriceFromString($columns[1]->getText());
128
        $priceWithDiscount = $this->getPriceFromString($columns[3]->getText());
129
        $discount = $priceWithoutDiscount - $priceWithDiscount;
130
131
        return $discount === $amount;
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function hasOrderTotal($total)
138
    {
139
        if (!$this->hasElement('order_total')) {
140
            return false;
141
        }
142
143
        return $this->getTotalFromString($this->getElement('order_total')->getText()) === $total;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function addNotes($notes)
150
    {
151
        $this->getElement('extra_notes')->setValue($notes);
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function hasPromotionTotal($promotionTotal)
158
    {
159
        return false !== strpos($this->getElement('promotion_total')->getText(), $promotionTotal);
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function hasPromotion($promotionName)
166
    {
167
        return false !== stripos($this->getElement('promotion_discounts')->getText(), $promotionName);
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function hasTaxTotal($taxTotal)
174
    {
175
        return false !== strpos($this->getElement('tax_total')->getText(), $taxTotal);
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function hasShippingTotal($price)
182
    {
183
        return false !== strpos($this->getElement('shipping_total')->getText(), $price);
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function hasProductUnitPrice(ProductInterface $product, $price)
190
    {
191
        $productRowElement = $this->getProductRowElement($product);
192
193
        return null !== $productRowElement->find('css', sprintf('td:contains("%s")', $price));
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199
    public function hasProductOutOfStockValidationMessage(ProductInterface $product)
200
    {
201
        $message = sprintf('%s does not have sufficient stock.', $product->getName());
202
203
        return $this->getElement('validation_errors')->getText() === $message;
204
    }
205
206
    public function confirmOrder()
207
    {
208
        $this->getDocument()->pressButton('Place order');
209
    }
210
211
    public function changeAddress()
212
    {
213
        $this->getElement('addressing_step_label')->click();
214
    }
215
216
    public function changeShippingMethod()
217
    {
218
        $this->getElement('shipping_step_label')->click();
219
    }
220
221
    public function changePaymentMethod()
222
    {
223
        $this->getElement('payment_step_label')->click();
224
    }
225
226
    /**
227
     * {@inheritdoc}
228
     */
229
    protected function getDefinedElements()
230
    {
231
        return array_merge(parent::getDefinedElements(), [
232
            'addressing_step_label' => '.steps a:contains("Address")',
233
            'billing_address' => '#billing-address',
234
            'extra_notes' =>'#sylius_checkout_complete_notes',
235
            'items_table' => '#sylius-order',
236
            'order_total' => 'td:contains("Total")',
237
            'payment_method' => '#payment-method',
238
            'payment_step_label' => '.steps a:contains("Payment")',
239
            'product_row' => 'tbody tr:contains("%name%")',
240
            'promotion_discounts' => '#promotion-discounts',
241
            'promotion_total' => '#promotion-total',
242
            'shipping_address' => '#shipping-address',
243
            'shipping_method' => '#shipping-method',
244
            'shipping_step_label' => '.steps a:contains("Shipping")',
245
            'shipping_total' => '#shipping-total',
246
            'tax_total' => '#tax-total',
247
            'validation_errors' => '.sylius-validation-error',
248
        ]);
249
    }
250
251
    /**
252
     * @param ProductInterface $product
253
     *
254
     * @return NodeElement
255
     */
256
    private function getProductRowElement(ProductInterface $product)
257
    {
258
        return $this->getElement('product_row', ['%name%' => $product->getName()]);
259
    }
260
261
    /**
262
     * @param string $displayedAddress
263
     * @param AddressInterface $address
264
     *
265
     * @return bool
266
     */
267
    private function isAddressValid($displayedAddress, AddressInterface $address)
268
    {
269
        return
270
            $this->hasAddressPart($displayedAddress, $address->getCompany(), true) &&
271
            $this->hasAddressPart($displayedAddress, $address->getFirstName()) &&
272
            $this->hasAddressPart($displayedAddress, $address->getLastName()) &&
273
            $this->hasAddressPart($displayedAddress, $address->getPhoneNumber(), true) &&
274
            $this->hasAddressPart($displayedAddress, $address->getStreet()) &&
275
            $this->hasAddressPart($displayedAddress, $address->getCity()) &&
276
            $this->hasAddressPart($displayedAddress, $address->getProvinceCode(), true) &&
277
            $this->hasAddressPart($displayedAddress, $this->getCountryName($address->getCountryCode())) &&
278
            $this->hasAddressPart($displayedAddress, $address->getPostcode())
279
        ;
280
    }
281
282
    /**
283
     * @param string $address
284
     * @param string $addressPart
285
     *
286
     * @return bool
287
     */
288
    private function hasAddressPart($address, $addressPart, $optional = false)
289
    {
290
        if ($optional && null === $addressPart) {
291
            return true;
292
        }
293
294
        return false !== strpos($address, $addressPart);
295
    }
296
297
    /**
298
     * @param string $countryCode
299
     *
300
     * @return string
301
     */
302
    private function getCountryName($countryCode)
303
    {
304
        return strtoupper(Intl::getRegionBundle()->getCountryName($countryCode, 'en'));
305
    }
306
307
    /**
308
     * @param string $price
309
     *
310
     * @return int
311
     */
312
    private function getPriceFromString($price)
313
    {
314
        return (int) round((str_replace(['€', '£', '$'], '', $price) * 100), 2);
315
    }
316
317
    /**
318
     * @param string $total
319
     *
320
     * @return int
321
     */
322
    private function getTotalFromString($total)
323
    {
324
        $total = str_replace('Total:', '', $total);
325
326
        return $this->getPriceFromString($total);
327
    }
328
}
329