Completed
Push — master ( 881b3b...090ef2 )
by Michał
10:07
created

ShowPage   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 517
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Importance

Changes 0
Metric Value
wmc 56
lcom 2
cbo 5
dl 0
loc 517
rs 6.5957
c 0
b 0
f 0

52 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A hasCustomer() 0 6 1
A hasShippingAddress() 0 6 1
A hasBillingAddress() 0 6 1
A hasShipment() 0 6 1
A specifyTrackingCode() 0 4 1
A canShipOrder() 0 4 1
A shipOrder() 0 4 1
A hasPayment() 0 6 1
A canCompleteOrderLastPayment() 0 4 1
A completeOrderLastPayment() 0 4 1
A refundOrderLastPayment() 0 4 1
A countItems() 0 4 1
A isProductInTheList() 0 13 2
A getItemsTotal() 0 6 1
A getTotal() 0 6 1
A getShippingTotal() 0 6 1
A getTaxTotal() 0 6 1
A hasShippingCharge() 0 6 1
A getPromotionTotal() 0 6 1
A hasPromotionDiscount() 0 6 1
A hasShippingPromotion() 0 4 1
A hasTax() 0 6 1
A getItemCode() 0 4 1
A getItemUnitPrice() 0 4 1
A getItemDiscountedUnitPrice() 0 4 1
A getItemQuantity() 0 4 1
A getItemSubtotal() 0 4 1
A getItemDiscount() 0 4 1
A getItemTax() 0 4 1
A getItemTotal() 0 4 1
A getPaymentAmount() 0 6 1
A getPaymentsCount() 0 6 1
A hasCancelButton() 0 4 1
A getOrderState() 0 4 1
A getPaymentState() 0 4 1
A cancelOrder() 0 4 1
A deleteOrder() 0 4 1
A hasNote() 0 6 1
A hasShippingProvinceName() 0 6 1
A hasBillingProvinceName() 0 6 1
A getIpAddressAssigned() 0 4 1
A getOrderCurrency() 0 4 1
A hasRefundButton() 0 4 1
A getShippingPromotionData() 0 4 1
A getRouteName() 0 4 1
B getDefinedElements() 0 25 1
A getTableAccessor() 0 4 1
A hasAddress() 0 9 4
A getItemProperty() 0 9 1
A getLastOrderPaymentElement() 0 9 1
A getLastOrderShipmentElement() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like ShowPage often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ShowPage, and based on these observations, apply Extract Interface, too.

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\Admin\Order;
13
14
use Behat\Mink\Element\NodeElement;
15
use Behat\Mink\Session;
16
use Sylius\Behat\Page\SymfonyPage;
17
use Sylius\Behat\Service\Accessor\TableAccessorInterface;
18
use Sylius\Component\Core\Model\OrderInterface;
19
use Symfony\Component\Routing\RouterInterface;
20
21
/**
22
 * @author Łukasz Chruściel <[email protected]>
23
 * @author Grzegorz Sadowski <[email protected]>
24
 */
25
class ShowPage extends SymfonyPage implements ShowPageInterface
26
{
27
    /**
28
     * @var TableAccessorInterface
29
     */
30
    private $tableAccessor;
31
32
    /**
33
     * @param Session $session
34
     * @param array $parameters
35
     * @param RouterInterface $router
36
     * @param TableAccessorInterface $tableAccessor
37
     */
38
    public function __construct(
39
        Session $session,
40
        array $parameters,
41
        RouterInterface $router,
42
        TableAccessorInterface $tableAccessor
43
    ) {
44
        parent::__construct($session, $parameters, $router);
45
46
        $this->tableAccessor = $tableAccessor;
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function hasCustomer($customerName)
53
    {
54
        $customerText = $this->getElement('customer')->getText();
55
56
        return stripos($customerText, $customerName) !== false;
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function hasShippingAddress($customerName, $street, $postcode, $city, $countryName)
63
    {
64
        $shippingAddressText = $this->getElement('shipping_address')->getText();
65
66
        return $this->hasAddress($shippingAddressText, $customerName, $street, $postcode, $city, $countryName);
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function hasBillingAddress($customerName, $street, $postcode, $city, $countryName)
73
    {
74
        $billingAddressText = $this->getElement('billing_address')->getText();
75
76
        return $this->hasAddress($billingAddressText, $customerName, $street, $postcode, $city, $countryName);
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function hasShipment($shippingDetails)
83
    {
84
        $shipmentsText = $this->getElement('shipments')->getText();
85
86
        return stripos($shipmentsText, $shippingDetails) !== false;
87
    }
88
89
    public function specifyTrackingCode($code)
90
    {
91
        $this->getDocument()->fillField('sylius_shipment_ship_tracking', $code);
92
    }
93
94
    public function canShipOrder(OrderInterface $order)
95
    {
96
        return $this->getLastOrderShipmentElement($order)->hasButton('Ship');
97
    }
98
99
    public function shipOrder(OrderInterface $order)
100
    {
101
        $this->getLastOrderShipmentElement($order)->pressButton('Ship');
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function hasPayment($paymentDetails)
108
    {
109
        $paymentsText = $this->getElement('payments')->getText();
110
111
        return stripos($paymentsText, $paymentDetails) !== false;
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function canCompleteOrderLastPayment(OrderInterface $order)
118
    {
119
        return $this->getLastOrderPaymentElement($order)->hasButton('Complete');
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function completeOrderLastPayment(OrderInterface $order)
126
    {
127
        $this->getLastOrderPaymentElement($order)->pressButton('Complete');
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function refundOrderLastPayment(OrderInterface $order)
134
    {
135
        $this->getLastOrderPaymentElement($order)->pressButton('Refund');
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function countItems()
142
    {
143
        return $this->tableAccessor->countTableBodyRows($this->getElement('table'));
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function isProductInTheList($productName)
150
    {
151
        try {
152
            $rows = $this->tableAccessor->getRowsWithFields(
153
                $this->getElement('table'),
154
                ['item' => $productName]
155
            );
156
157
            return 1 === count($rows);
158
        } catch (\InvalidArgumentException $exception) {
159
            return false;
160
        }
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function getItemsTotal()
167
    {
168
        $itemsTotalElement = $this->getElement('items_total');
169
170
        return trim(str_replace('Subtotal:', '', $itemsTotalElement->getText()));
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176
    public function getTotal()
177
    {
178
        $totalElement = $this->getElement('total');
179
180
        return trim(str_replace('Total:', '', $totalElement->getText()));
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186
    public function getShippingTotal()
187
    {
188
        $shippingTotalElement = $this->getElement('shipping_total');
189
190
        return trim(str_replace('Shipping total:', '', $shippingTotalElement->getText()));
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function getTaxTotal()
197
    {
198
        $taxTotalElement = $this->getElement('tax_total');
199
200
        return trim(str_replace('Tax total:', '', $taxTotalElement->getText()));
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function hasShippingCharge($shippingCharge)
207
    {
208
        $shippingChargesText = $this->getElement('shipping_charges')->getText();
209
210
        return stripos($shippingChargesText, $shippingCharge) !== false;
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function getPromotionTotal()
217
    {
218
        $promotionTotalElement = $this->getElement('promotion_total');
219
220
        return trim(str_replace('Promotion total:', '', $promotionTotalElement->getText()));
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226
    public function hasPromotionDiscount($promotionDiscount)
227
    {
228
        $promotionDiscountsText = $this->getElement('promotion_discounts')->getText();
229
230
        return stripos($promotionDiscountsText, $promotionDiscount) !== false;
231
    }
232
233
    /**
234
     * {@inheritdoc}
235
     */
236
    public function hasShippingPromotion($promotionName)
237
    {
238
        return $this->getElement('promotion_shipping_discounts')->getText();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getElement...discounts')->getText(); (string) is incompatible with the return type declared by the interface Sylius\Behat\Page\Admin\...e::hasShippingPromotion of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
239
    }
240
241
    /**
242
     * {@inheritdoc}
243
     */
244
    public function hasTax($tax)
245
    {
246
        $taxesText = $this->getElement('taxes')->getText();
247
248
        return stripos($taxesText, $tax) !== false;
249
    }
250
251
    /**
252
     * {@inheritdoc}
253
     */
254
    public function getItemCode($itemName)
255
    {
256
        return $this->getItemProperty($itemName, 'sylius-product-variant-code');
257
    }
258
259
    /**
260
     * {@inheritdoc}
261
     */
262
    public function getItemUnitPrice($itemName)
263
    {
264
        return $this->getItemProperty($itemName, 'unit-price');
265
    }
266
267
    /**
268
     * {@inheritdoc}
269
     */
270
    public function getItemDiscountedUnitPrice($itemName)
271
    {
272
        return $this->getItemProperty($itemName, 'discounted-unit-price');
273
    }
274
275
    /**
276
     * {@inheritdoc}
277
     */
278
    public function getItemQuantity($itemName)
279
    {
280
        return $this->getItemProperty($itemName, 'quantity');
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286
    public function getItemSubtotal($itemName)
287
    {
288
        return $this->getItemProperty($itemName, 'subtotal');
289
    }
290
291
    /**
292
     * {@inheritdoc}
293
     */
294
    public function getItemDiscount($itemName)
295
    {
296
        return $this->getItemProperty($itemName, 'discount');
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302
    public function getItemTax($itemName)
303
    {
304
        return $this->getItemProperty($itemName, 'tax');
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    public function getItemTotal($itemName)
311
    {
312
        return $this->getItemProperty($itemName, 'total');
313
    }
314
315
    /**
316
     * {@inheritdoc}
317
     */
318
    public function getPaymentAmount()
319
    {
320
        $paymentsPrice = $this->getElement('payments')->find('css', '.description');
321
322
        return $paymentsPrice->getText();
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328
    public function getPaymentsCount()
329
    {
330
        $payments = $this->getElement('payments')->findAll('css', '.item');
331
332
        return count($payments);
333
    }
334
335
    /**
336
     * {@inheritdoc}
337
     */
338
    public function hasCancelButton()
339
    {
340
        return $this->getDocument()->hasButton('Cancel');
341
    }
342
343
    /**
344
     * {@inheritdoc}
345
     */
346
    public function getOrderState()
347
    {
348
        return $this->getElement('order_state')->getText();
349
    }
350
351
    /**
352
     * {@inheritdoc}
353
     */
354
    public function getPaymentState()
355
    {
356
        return $this->getElement('order_payment_state')->getText();
357
    }
358
359
    public function cancelOrder()
360
    {
361
        $this->getDocument()->pressButton('Cancel');
362
    }
363
364
    public function deleteOrder()
365
    {
366
        $this->getDocument()->pressButton('Delete');
367
    }
368
369
    /**
370
     * {@inheritdoc}
371
     */
372
    public function hasNote($note)
373
    {
374
        $orderNotesElement = $this->getElement('order_notes');
375
376
        return $orderNotesElement->getText() === $note;
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    public function hasShippingProvinceName($provinceName)
383
    {
384
        $shippingAddressText = $this->getElement('shipping_address')->getText();
385
386
        return false !== stripos($shippingAddressText, $provinceName);
387
    }
388
389
    /**
390
     * {@inheritdoc}
391
     */
392
    public function hasBillingProvinceName($provinceName)
393
    {
394
        $billingAddressText = $this->getElement('billing_address')->getText();
395
396
        return false !== stripos($billingAddressText, $provinceName);
397
    }
398
399
    /**
400
     * {@inheritdoc}
401
     */
402
    public function getIpAddressAssigned()
403
    {
404
        return $this->getElement('ip_address')->getText();
405
    }
406
407
    /**
408
     * {@inheritdoc}
409
     */
410
    public function getOrderCurrency()
411
    {
412
        return $this->getElement('currency')->getText();
413
    }
414
415
    /**
416
     * {@inheritdoc}
417
     */
418
    public function hasRefundButton()
419
    {
420
        return $this->getDocument()->hasButton('Refund');
421
    }
422
423
    /**
424
     * {@inheritdoc}
425
     */
426
    public function getShippingPromotionData()
427
    {
428
        return $this->getElement('promotion_shipping_discounts')->getText();
429
    }
430
431
    /**
432
     * {@inheritdoc}
433
     */
434
    public function getRouteName()
435
    {
436
        return 'sylius_admin_order_show';
437
    }
438
439
    /**
440
     * {@inheritdoc}
441
     */
442
    protected function getDefinedElements()
443
    {
444
        return array_merge(parent::getDefinedElements(), [
445
            'billing_address' => '#billing-address',
446
            'currency' => '#sylius-order-currency',
447
            'customer' => '#customer',
448
            'ip_address' => '#ipAddress',
449
            'items_total' => '#items-total',
450
            'order_notes' => '#sylius-order-notes',
451
            'order_payment_state' => '#payment-state > span',
452
            'order_state' => '#sylius-order-state',
453
            'payments' => '#sylius-payments',
454
            'promotion_discounts' => '#promotion-discounts',
455
            'promotion_shipping_discounts' => '#promotion-shipping-discounts',
456
            'promotion_total' => '#promotion-total',
457
            'shipments' => '#sylius-shipments',
458
            'shipping_address' => '#shipping-address',
459
            'shipping_charges' => '#shipping-charges',
460
            'shipping_total' => '#shipping-total',
461
            'table' => '.table',
462
            'tax_total' => '#tax-total',
463
            'taxes' => '#taxes',
464
            'total' => '#total',
465
        ]);
466
    }
467
468
    /**
469
     * @return TableAccessorInterface
470
     */
471
    protected function getTableAccessor()
472
    {
473
        return $this->tableAccessor;
474
    }
475
476
    /**
477
     * @param string $elementText
478
     * @param string $customerName
479
     * @param string $street
480
     * @param string $postcode
481
     * @param string $city
482
     * @param string $countryName
483
     *
484
     * @return bool
485
     */
486
    private function hasAddress($elementText, $customerName, $street, $postcode, $city, $countryName)
487
    {
488
        return
489
            (stripos($elementText, $customerName) !== false) &&
490
            (stripos($elementText, $street) !== false) &&
491
            (stripos($elementText, $city) !== false) &&
492
            (stripos($elementText, $countryName.' '.$postcode) !== false)
493
        ;
494
    }
495
496
    /**
497
     * @param string $itemName
498
     * @param string $property
499
     *
500
     * @return string
501
     */
502
    private function getItemProperty($itemName, $property)
503
    {
504
        $rows = $this->tableAccessor->getRowsWithFields(
505
            $this->getElement('table'),
506
            ['item' => $itemName]
507
        );
508
509
        return $rows[0]->find('css', '.'.$property)->getText();
510
    }
511
512
    /**
513
     * @param OrderInterface $order
514
     *
515
     * @return NodeElement|null
516
     */
517
    private function getLastOrderPaymentElement(OrderInterface $order)
518
    {
519
        $payment = $order->getPayments()->last();
520
521
        $paymentStateElements = $this->getElement('payments')->findAll('css', sprintf('span.ui.label:contains(\'%s\')', ucfirst($payment->getState())));
522
        $paymentStateElement = end($paymentStateElements);
523
524
        return $paymentStateElement->getParent()->getParent();
525
    }
526
527
    /**
528
     * @param OrderInterface $order
529
     *
530
     * @return NodeElement|null
531
     */
532
    private function getLastOrderShipmentElement(OrderInterface $order)
533
    {
534
        $shipment = $order->getShipments()->last();
535
536
        $shipmentStateElements = $this->getElement('shipments')->findAll('css', sprintf('span.ui.label:contains(\'%s\')', ucfirst($shipment->getState())));
537
        $shipmentStateElement = end($shipmentStateElements);
538
539
        return $shipmentStateElement->getParent()->getParent();
540
    }
541
}
542