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\Context\Ui\Admin; |
13
|
|
|
|
14
|
|
|
use Behat\Behat\Context\Context; |
15
|
|
|
use Sylius\Behat\NotificationType; |
16
|
|
|
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface; |
17
|
|
|
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface; |
18
|
|
|
use Sylius\Behat\Page\Admin\Product\CreateConfigurableProductPageInterface; |
19
|
|
|
use Sylius\Behat\Page\Admin\Product\CreateSimpleProductPageInterface; |
20
|
|
|
use Sylius\Behat\Page\Admin\Product\IndexPageInterface; |
21
|
|
|
use Sylius\Behat\Page\Admin\Product\UpdateConfigurableProductPageInterface; |
22
|
|
|
use Sylius\Behat\Page\Admin\Product\UpdateSimpleProductPageInterface; |
23
|
|
|
use Sylius\Behat\Page\Admin\ProductReview\IndexPageInterface as ProductReviewIndexPageInterface; |
24
|
|
|
use Sylius\Behat\Service\NotificationCheckerInterface; |
25
|
|
|
use Sylius\Behat\Service\Resolver\CurrentProductPageResolverInterface; |
26
|
|
|
use Sylius\Component\Core\Model\ChannelInterface; |
27
|
|
|
use Sylius\Component\Core\Model\ProductInterface; |
28
|
|
|
use Sylius\Behat\Service\SharedStorageInterface; |
29
|
|
|
use Sylius\Component\Currency\Model\CurrencyInterface; |
30
|
|
|
use Sylius\Component\Product\Model\ProductAssociationTypeInterface; |
31
|
|
|
use Sylius\Component\Taxonomy\Model\TaxonInterface; |
32
|
|
|
use Webmozart\Assert\Assert; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @author Kamil Kokot <[email protected]> |
36
|
|
|
* @author Magdalena Banasiak <[email protected]> |
37
|
|
|
* @author Łukasz Chruściel <[email protected]> |
38
|
|
|
*/ |
39
|
|
|
final class ManagingProductsContext implements Context |
40
|
|
|
{ |
41
|
|
|
/** |
42
|
|
|
* @var SharedStorageInterface |
43
|
|
|
*/ |
44
|
|
|
private $sharedStorage; |
45
|
|
|
|
46
|
|
|
/**x |
47
|
|
|
* @var CreateSimpleProductPageInterface |
48
|
|
|
*/ |
49
|
|
|
private $createSimpleProductPage; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var CreateConfigurableProductPageInterface |
53
|
|
|
*/ |
54
|
|
|
private $createConfigurableProductPage; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var IndexPageInterface |
58
|
|
|
*/ |
59
|
|
|
private $indexPage; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var UpdateSimpleProductPageInterface |
63
|
|
|
*/ |
64
|
|
|
private $updateSimpleProductPage; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var UpdateConfigurableProductPageInterface |
68
|
|
|
*/ |
69
|
|
|
private $updateConfigurableProductPage; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var ProductReviewIndexPageInterface |
73
|
|
|
*/ |
74
|
|
|
private $productReviewIndexPage; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @var CurrentProductPageResolverInterface |
78
|
|
|
*/ |
79
|
|
|
private $currentPageResolver; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @var NotificationCheckerInterface |
83
|
|
|
*/ |
84
|
|
|
private $notificationChecker; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @param SharedStorageInterface $sharedStorage |
88
|
|
|
* @param CreateSimpleProductPageInterface $createSimpleProductPage |
89
|
|
|
* @param CreateConfigurableProductPageInterface $createConfigurableProductPage |
90
|
|
|
* @param IndexPageInterface $indexPage |
91
|
|
|
* @param UpdateSimpleProductPageInterface $updateSimpleProductPage |
92
|
|
|
* @param UpdateConfigurableProductPageInterface $updateConfigurableProductPage |
93
|
|
|
* @param ProductReviewIndexPageInterface $productReviewIndexPage |
94
|
|
|
* @param CurrentProductPageResolverInterface $currentPageResolver |
95
|
|
|
* @param NotificationCheckerInterface $notificationChecker |
96
|
|
|
*/ |
97
|
|
|
public function __construct( |
98
|
|
|
SharedStorageInterface $sharedStorage, |
99
|
|
|
CreateSimpleProductPageInterface $createSimpleProductPage, |
100
|
|
|
CreateConfigurableProductPageInterface $createConfigurableProductPage, |
101
|
|
|
IndexPageInterface $indexPage, |
102
|
|
|
UpdateSimpleProductPageInterface $updateSimpleProductPage, |
103
|
|
|
UpdateConfigurableProductPageInterface $updateConfigurableProductPage, |
104
|
|
|
ProductReviewIndexPageInterface $productReviewIndexPage, |
105
|
|
|
CurrentProductPageResolverInterface $currentPageResolver, |
106
|
|
|
NotificationCheckerInterface $notificationChecker |
107
|
|
|
) { |
108
|
|
|
$this->sharedStorage = $sharedStorage; |
109
|
|
|
$this->createSimpleProductPage = $createSimpleProductPage; |
110
|
|
|
$this->createConfigurableProductPage = $createConfigurableProductPage; |
111
|
|
|
$this->indexPage = $indexPage; |
112
|
|
|
$this->updateSimpleProductPage = $updateSimpleProductPage; |
113
|
|
|
$this->updateConfigurableProductPage = $updateConfigurableProductPage; |
114
|
|
|
$this->productReviewIndexPage = $productReviewIndexPage; |
115
|
|
|
$this->currentPageResolver = $currentPageResolver; |
116
|
|
|
$this->notificationChecker = $notificationChecker; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @Given I want to create a new simple product |
121
|
|
|
*/ |
122
|
|
|
public function iWantToCreateANewSimpleProduct() |
123
|
|
|
{ |
124
|
|
|
$this->createSimpleProductPage->open(); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* @Given I want to create a new configurable product |
129
|
|
|
*/ |
130
|
|
|
public function iWantToCreateANewConfigurableProduct() |
131
|
|
|
{ |
132
|
|
|
$this->createConfigurableProductPage->open(); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @When I specify its code as :code |
137
|
|
|
* @When I do not specify its code |
138
|
|
|
*/ |
139
|
|
|
public function iSpecifyItsCodeAs($code = null) |
140
|
|
|
{ |
141
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
142
|
|
|
$this->createSimpleProductPage, |
143
|
|
|
$this->createConfigurableProductPage, |
144
|
|
|
]); |
145
|
|
|
|
146
|
|
|
$currentPage->specifyCode($code); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @When I name it :name in :language |
151
|
|
|
*/ |
152
|
|
|
public function iNameItIn($name, $language) |
153
|
|
|
{ |
154
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
155
|
|
|
$this->createSimpleProductPage, |
156
|
|
|
$this->createConfigurableProductPage, |
157
|
|
|
]); |
158
|
|
|
|
159
|
|
|
$currentPage->nameItIn($name, $language); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* @When I rename it to :name in :language |
164
|
|
|
*/ |
165
|
|
|
public function iRenameItToIn($name, $language) |
166
|
|
|
{ |
167
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
168
|
|
|
$this->updateSimpleProductPage, |
169
|
|
|
$this->updateConfigurableProductPage, |
170
|
|
|
], $this->sharedStorage->get('product')); |
171
|
|
|
|
172
|
|
|
$currentPage->nameItIn($name, $language); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @When I add it |
177
|
|
|
* @When I try to add it |
178
|
|
|
*/ |
179
|
|
|
public function iAddIt() |
180
|
|
|
{ |
181
|
|
|
/** @var CreatePageInterface $currentPage */ |
182
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
183
|
|
|
$this->createSimpleProductPage, |
184
|
|
|
$this->createConfigurableProductPage, |
185
|
|
|
]); |
186
|
|
|
|
187
|
|
|
Assert::isInstanceOf($currentPage, CreatePageInterface::class); |
188
|
|
|
|
189
|
|
|
$currentPage->create(); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @When I disable its inventory tracking |
194
|
|
|
*/ |
195
|
|
|
public function iDisableItsTracking() |
196
|
|
|
{ |
197
|
|
|
$this->updateSimpleProductPage->disableTracking(); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @When I enable its inventory tracking |
202
|
|
|
*/ |
203
|
|
|
public function iEnableItsTracking() |
204
|
|
|
{ |
205
|
|
|
$this->updateSimpleProductPage->enableTracking(); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @When /^I set its(?:| default) price to ("(?:€|£|\$)[^"]+")$/ |
210
|
|
|
*/ |
211
|
|
|
public function iSetItsPriceTo($price) |
212
|
|
|
{ |
213
|
|
|
$this->createSimpleProductPage->specifyPrice($price); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @When I choose :calculatorName calculator |
218
|
|
|
*/ |
219
|
|
|
public function iChooseCalculator($calculatorName) |
220
|
|
|
{ |
221
|
|
|
$this->createSimpleProductPage->choosePricingCalculator($calculatorName); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* @When /^I set its price to "(?:€|£|\$)([^"]+)" for ("[^"]+" currency) and ("[^"]+" channel)$/ |
226
|
|
|
*/ |
227
|
|
|
public function iSetItsPriceToForCurrencyAndChannel($price, CurrencyInterface $currency, ChannelInterface $channel) |
228
|
|
|
{ |
229
|
|
|
$this->createSimpleProductPage->specifyPriceForChannelAndCurrency($price, $channel, $currency); |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @When I set its slug to :slug |
234
|
|
|
* @When I remove its slug |
235
|
|
|
*/ |
236
|
|
|
public function iSetItsSlugTo($slug = null) |
237
|
|
|
{ |
238
|
|
|
$this->createSimpleProductPage->specifySlug($slug); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* @When I enable slug modification |
243
|
|
|
*/ |
244
|
|
|
public function iEnableSlugModification() |
245
|
|
|
{ |
246
|
|
|
$this->createSimpleProductPage->enableSlugModification(); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* @Then the product :productName should appear in the shop |
251
|
|
|
* @Then the product :productName should be in the shop |
252
|
|
|
* @Then this product should still be named :productName |
253
|
|
|
*/ |
254
|
|
|
public function theProductShouldAppearInTheShop($productName) |
255
|
|
|
{ |
256
|
|
|
$this->iWantToBrowseProducts(); |
257
|
|
|
|
258
|
|
|
Assert::true( |
259
|
|
|
$this->indexPage->isSingleResourceOnPage(['name' => $productName]), |
260
|
|
|
sprintf('The product with name %s has not been found.', $productName) |
261
|
|
|
); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @Given I am browsing products |
266
|
|
|
* @When I want to browse products |
267
|
|
|
*/ |
268
|
|
|
public function iWantToBrowseProducts() |
269
|
|
|
{ |
270
|
|
|
$this->indexPage->open(); |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* @When I filter them by :taxonName taxon |
275
|
|
|
*/ |
276
|
|
|
public function iFilterThemByTaxon($taxonName) |
277
|
|
|
{ |
278
|
|
|
$this->indexPage->filterByTaxon($taxonName); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* @Then I should( still) see a product with :field :value |
283
|
|
|
*/ |
284
|
|
|
public function iShouldSeeProductWith($field, $value) |
285
|
|
|
{ |
286
|
|
|
Assert::true( |
287
|
|
|
$this->indexPage->isSingleResourceOnPage([$field => $value]), |
288
|
|
|
sprintf('The product with %s "%s" has not been found.', $field, $value) |
289
|
|
|
); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* @Then I should not see any product with :field :value |
294
|
|
|
*/ |
295
|
|
|
public function iShouldNotSeeAnyProductWith($field, $value) |
296
|
|
|
{ |
297
|
|
|
Assert::false( |
298
|
|
|
$this->indexPage->isSingleResourceOnPage([$field => $value]), |
299
|
|
|
sprintf('The product with %s "%s" has been found.', $field, $value) |
300
|
|
|
); |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* @Then the first product on the list should have :field :value |
305
|
|
|
*/ |
306
|
|
|
public function theFirstProductOnTheListShouldHave($field, $value) |
307
|
|
|
{ |
308
|
|
|
$actualValue = $this->indexPage->getColumnFields($field)[0]; |
309
|
|
|
|
310
|
|
|
Assert::same( |
311
|
|
|
$actualValue, |
312
|
|
|
$value, |
313
|
|
|
sprintf('Expected first product\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue) |
314
|
|
|
); |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* @When I switch the way products are sorted by :field |
319
|
|
|
* @When I start sorting products by :field |
320
|
|
|
* @Given the products are already sorted by :field |
321
|
|
|
*/ |
322
|
|
|
public function iSortProductsBy($field) |
323
|
|
|
{ |
324
|
|
|
$this->indexPage->sortBy($field); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* @Then I should see :numberOfProducts products in the list |
329
|
|
|
*/ |
330
|
|
|
public function iShouldSeeProductsInTheList($numberOfProducts) |
331
|
|
|
{ |
332
|
|
|
$foundRows = $this->indexPage->countItems(); |
333
|
|
|
|
334
|
|
|
Assert::same( |
335
|
|
|
(int) $numberOfProducts, |
336
|
|
|
$foundRows, |
337
|
|
|
'%s rows with products should appear on page, %s rows has been found' |
338
|
|
|
); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* @When I delete the :product product |
343
|
|
|
* @When I try to delete the :product product |
344
|
|
|
*/ |
345
|
|
|
public function iDeleteProduct(ProductInterface $product) |
346
|
|
|
{ |
347
|
|
|
$this->sharedStorage->set('product', $product); |
348
|
|
|
|
349
|
|
|
$this->iWantToBrowseProducts(); |
350
|
|
|
$this->indexPage->deleteResourceOnPage(['name' => $product->getName()]); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* @Then /^(this product) should not exist in the product catalog$/ |
355
|
|
|
*/ |
356
|
|
|
public function productShouldNotExist(ProductInterface $product) |
357
|
|
|
{ |
358
|
|
|
$this->iWantToBrowseProducts(); |
359
|
|
|
|
360
|
|
|
Assert::false( |
361
|
|
|
$this->indexPage->isSingleResourceOnPage(['code' => $product->getCode()]), |
362
|
|
|
sprintf('Product with code %s exists but should not.', $product->getCode()) |
363
|
|
|
); |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* @Then I should be notified that this product is in use and cannot be deleted |
368
|
|
|
*/ |
369
|
|
|
public function iShouldBeNotifiedOfFailure() |
370
|
|
|
{ |
371
|
|
|
$this->notificationChecker->checkNotification( |
372
|
|
|
"Cannot delete, the product is in use.", |
|
|
|
|
373
|
|
|
NotificationType::failure() |
374
|
|
|
); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* @Then /^(this product) should still exist in the product catalog$/ |
379
|
|
|
*/ |
380
|
|
|
public function productShouldExistInTheProductCatalog(ProductInterface $product) |
381
|
|
|
{ |
382
|
|
|
$this->theProductShouldAppearInTheShop($product->getName()); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* @When I want to modify the :product product |
387
|
|
|
* @When /^I want to modify (this product)$/ |
388
|
|
|
*/ |
389
|
|
|
public function iWantToModifyAProduct(ProductInterface $product) |
390
|
|
|
{ |
391
|
|
|
$this->sharedStorage->set('product', $product); |
392
|
|
|
|
393
|
|
|
if ($product->isSimple()) { |
394
|
|
|
$this->updateSimpleProductPage->open(['id' => $product->getId()]); |
395
|
|
|
return; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
$this->updateConfigurableProductPage->open(['id' => $product->getId()]); |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
/** |
402
|
|
|
* @Then the code field should be disabled |
403
|
|
|
*/ |
404
|
|
|
public function theCodeFieldShouldBeDisabled() |
405
|
|
|
{ |
406
|
|
|
/** @var UpdatePageInterface $currentPage */ |
407
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
408
|
|
|
$this->updateSimpleProductPage, |
409
|
|
|
$this->updateConfigurableProductPage, |
410
|
|
|
], $this->sharedStorage->get('product')); |
411
|
|
|
|
412
|
|
|
Assert::true( |
413
|
|
|
$currentPage->isCodeDisabled(), |
414
|
|
|
'Code should be immutable, but it does not.' |
415
|
|
|
); |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* @Then the slug field should not be editable |
420
|
|
|
*/ |
421
|
|
|
public function theSlugFieldShouldNotBeEditable() |
422
|
|
|
{ |
423
|
|
|
Assert::true( |
424
|
|
|
$this->updateSimpleProductPage->isSlugReadOnly(), |
425
|
|
|
'Slug should be immutable, but it does not.' |
426
|
|
|
); |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* @Then /^this product price should be "(?:€|£|\$)([^"]+)"$/ |
431
|
|
|
*/ |
432
|
|
|
public function thisProductPriceShouldBeEqualTo($price) |
433
|
|
|
{ |
434
|
|
|
$this->assertElementValue('price', $price); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
/** |
438
|
|
|
* @Then this product name should be :name |
439
|
|
|
*/ |
440
|
|
|
public function thisProductElementShouldBe($name) |
441
|
|
|
{ |
442
|
|
|
$this->assertElementValue('name', $name); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
/** |
446
|
|
|
* @Then /^I should be notified that (code|name|slug) is required$/ |
447
|
|
|
*/ |
448
|
|
|
public function iShouldBeNotifiedThatIsRequired($element) |
449
|
|
|
{ |
450
|
|
|
$this->assertValidationMessage($element, sprintf('Please enter product %s.', $element)); |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* @Then I should be notified that price is required |
455
|
|
|
*/ |
456
|
|
|
public function iShouldBeNotifiedThatPriceIsRequired() |
457
|
|
|
{ |
458
|
|
|
$this->assertValidationMessage('price', 'Please enter the price.'); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* @When I save my changes |
463
|
|
|
* @When I try to save my changes |
464
|
|
|
*/ |
465
|
|
|
public function iSaveMyChanges() |
466
|
|
|
{ |
467
|
|
|
/** @var UpdatePageInterface $currentPage */ |
468
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
469
|
|
|
$this->updateSimpleProductPage, |
470
|
|
|
$this->updateConfigurableProductPage, |
471
|
|
|
], $this->sharedStorage->get('product')); |
472
|
|
|
|
473
|
|
|
Assert::isInstanceOf($currentPage, UpdatePageInterface::class); |
474
|
|
|
|
475
|
|
|
$currentPage->saveChanges(); |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* @When /^I change its price to "(?:€|£|\$)([^"]+)"$/ |
480
|
|
|
*/ |
481
|
|
|
public function iChangeItsPriceTo($price) |
482
|
|
|
{ |
483
|
|
|
$this->updateSimpleProductPage->specifyPrice($price); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* @Given I add the :optionName option to it |
488
|
|
|
*/ |
489
|
|
|
public function iAddTheOptionToIt($optionName) |
490
|
|
|
{ |
491
|
|
|
$this->createConfigurableProductPage->selectOption($optionName); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* @When I set its :attribute attribute to :value |
496
|
|
|
*/ |
497
|
|
|
public function iSetItsAttributeTo($attribute, $value) |
498
|
|
|
{ |
499
|
|
|
$this->createSimpleProductPage->addAttribute($attribute, $value); |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
/** |
503
|
|
|
* @When I remove its :attribute attribute |
504
|
|
|
*/ |
505
|
|
|
public function iRemoveItsAttribute($attribute) |
506
|
|
|
{ |
507
|
|
|
$this->createSimpleProductPage->removeAttribute($attribute); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
/** |
511
|
|
|
* @Then /^attribute "([^"]+)" of (product "[^"]+") should be "([^"]+)"$/ |
512
|
|
|
*/ |
513
|
|
|
public function itsAttributeShouldBe($attribute, ProductInterface $product, $value) |
514
|
|
|
{ |
515
|
|
|
$this->updateSimpleProductPage->open(['id' => $product->getId()]); |
516
|
|
|
|
517
|
|
|
Assert::same( |
518
|
|
|
$value, |
519
|
|
|
$this->updateSimpleProductPage->getAttributeValue($attribute), |
520
|
|
|
sprintf('ProductAttribute "%s" should have value "%s" but it does not.', $attribute, $value) |
521
|
|
|
); |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* @Then /^(product "[^"]+") should not have a "([^"]+)" attribute$/ |
526
|
|
|
*/ |
527
|
|
|
public function productShouldNotHaveAttribute(ProductInterface $product, $attribute) |
528
|
|
|
{ |
529
|
|
|
$this->updateSimpleProductPage->open(['id' => $product->getId()]); |
530
|
|
|
|
531
|
|
|
Assert::false( |
532
|
|
|
$this->updateSimpleProductPage->hasAttribute($attribute), |
533
|
|
|
sprintf('Product "%s" should not have attribute "%s" but it does.', $product->getName(), $attribute) |
534
|
|
|
); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* @Given product with :element :value should not be added |
539
|
|
|
*/ |
540
|
|
|
public function productWithNameShouldNotBeAdded($element, $value) |
541
|
|
|
{ |
542
|
|
|
$this->iWantToBrowseProducts(); |
543
|
|
|
|
544
|
|
|
Assert::false( |
545
|
|
|
$this->indexPage->isSingleResourceOnPage([$element => $value]), |
546
|
|
|
sprintf('Product with %s %s was created, but it should not.', $element, $value) |
547
|
|
|
); |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
/** |
551
|
|
|
* @When I remove its name from :language translation |
552
|
|
|
*/ |
553
|
|
|
public function iRemoveItsNameFromTranslation($language) |
554
|
|
|
{ |
555
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
556
|
|
|
$this->updateSimpleProductPage, |
557
|
|
|
$this->updateConfigurableProductPage, |
558
|
|
|
], $this->sharedStorage->get('product')); |
559
|
|
|
|
560
|
|
|
$currentPage->nameItIn('', $language); |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
/** |
564
|
|
|
* @Then /^this product should have (?:a|an) "([^"]+)" option$/ |
565
|
|
|
*/ |
566
|
|
|
public function thisProductShouldHaveOption($productOption) |
567
|
|
|
{ |
568
|
|
|
$this->updateConfigurableProductPage->isProductOptionChosen($productOption); |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
/** |
572
|
|
|
* @Then the option field should be disabled |
573
|
|
|
*/ |
574
|
|
|
public function theOptionFieldShouldBeDisabled() |
575
|
|
|
{ |
576
|
|
|
Assert::true( |
577
|
|
|
$this->updateConfigurableProductPage->isProductOptionsDisabled(), |
578
|
|
|
'Options field should be immutable, but it does not.' |
579
|
|
|
); |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
/** |
583
|
|
|
* @When /^I choose main (taxon "([^"]+)")$/ |
584
|
|
|
*/ |
585
|
|
|
public function iChooseMainTaxon(TaxonInterface $taxon) |
586
|
|
|
{ |
587
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
588
|
|
|
$this->updateSimpleProductPage, |
589
|
|
|
$this->updateConfigurableProductPage, |
590
|
|
|
], $this->sharedStorage->get('product')); |
591
|
|
|
|
592
|
|
|
$currentPage->selectMainTaxon($taxon); |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
/** |
596
|
|
|
* @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)"$/ |
597
|
|
|
*/ |
598
|
|
|
public function productSlugShouldBe(ProductInterface $product, $slug) |
599
|
|
|
{ |
600
|
|
|
$this->updateSimpleProductPage->open(['id' => $product->getId()]); |
601
|
|
|
|
602
|
|
|
Assert::true( |
603
|
|
|
$this->updateSimpleProductPage->hasResourceValues(['slug' => $slug]), |
604
|
|
|
sprintf('Product\'s slug should be %s.', $slug) |
605
|
|
|
); |
606
|
|
|
} |
607
|
|
|
|
608
|
|
|
/** |
609
|
|
|
* @Then /^(this product) main taxon should be "([^"]+)"$/ |
610
|
|
|
*/ |
611
|
|
|
public function thisProductMainTaxonShouldBe(ProductInterface $product, $taxonName) |
612
|
|
|
{ |
613
|
|
|
/** @var UpdatePageInterface $currentPage */ |
614
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
615
|
|
|
$this->updateSimpleProductPage, |
616
|
|
|
$this->updateConfigurableProductPage, |
617
|
|
|
], $this->sharedStorage->get('product')); |
618
|
|
|
|
619
|
|
|
$currentPage->open(['id' => $product->getId()]); |
620
|
|
|
|
621
|
|
|
Assert::true( |
622
|
|
|
$this->updateConfigurableProductPage->isMainTaxonChosen($taxonName), |
623
|
|
|
sprintf('The main taxon %s should be chosen, but it does not.', $taxonName) |
624
|
|
|
); |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
/** |
628
|
|
|
* @Then /^inventory of (this product) should not be tracked$/ |
629
|
|
|
*/ |
630
|
|
|
public function thisProductShouldNotBeTracked(ProductInterface $product) |
631
|
|
|
{ |
632
|
|
|
$this->iWantToModifyAProduct($product); |
633
|
|
|
|
634
|
|
|
Assert::false( |
635
|
|
|
$this->updateSimpleProductPage->isTracked(), |
636
|
|
|
'"%s" should not be tracked, but it is.' |
637
|
|
|
); |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* @Then /^inventory of (this product) should be tracked$/ |
642
|
|
|
*/ |
643
|
|
|
public function thisProductShouldBeTracked(ProductInterface $product) |
644
|
|
|
{ |
645
|
|
|
$this->iWantToModifyAProduct($product); |
646
|
|
|
|
647
|
|
|
Assert::true( |
648
|
|
|
$this->updateSimpleProductPage->isTracked(), |
649
|
|
|
'"%s" should be tracked, but it is not.' |
650
|
|
|
); |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
/** |
654
|
|
|
* @When I attach the :path image with a code :code |
655
|
|
|
*/ |
656
|
|
|
public function iAttachImageWithACode($path, $code) |
657
|
|
|
{ |
658
|
|
|
/** @var CreatePageInterface|UpdatePageInterface $currentPage */ |
659
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
660
|
|
|
$this->createSimpleProductPage, |
661
|
|
|
$this->createConfigurableProductPage, |
662
|
|
|
$this->updateSimpleProductPage, |
663
|
|
|
$this->updateConfigurableProductPage, |
664
|
|
|
], $this->sharedStorage->has('product') ? $this->sharedStorage->get('product') : null); |
665
|
|
|
|
666
|
|
|
$currentPage->attachImage($path, $code); |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
/** |
670
|
|
|
* @When I attach the :path image without a code |
671
|
|
|
*/ |
672
|
|
|
public function iAttachImageWithoutACode($path) |
673
|
|
|
{ |
674
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
675
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
676
|
|
|
$this->updateSimpleProductPage, |
677
|
|
|
$this->updateConfigurableProductPage, |
678
|
|
|
], $this->sharedStorage->get('product')); |
679
|
|
|
|
680
|
|
|
$currentPage->attachImage($path); |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
/** |
684
|
|
|
* @When I associate as :productAssociationType the :productName product |
685
|
|
|
* @When I associate as :productAssociationType the :firstProductName and :secondProductName products |
686
|
|
|
*/ |
687
|
|
|
public function iAssociateProductsAsProductAssociation( |
688
|
|
|
ProductAssociationTypeInterface $productAssociationType, |
689
|
|
|
...$productsNames |
690
|
|
|
) { |
691
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
692
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
693
|
|
|
$this->createSimpleProductPage, |
694
|
|
|
$this->updateSimpleProductPage, |
695
|
|
|
], $this->sharedStorage->get('product')); |
696
|
|
|
|
697
|
|
|
$currentPage->associateProducts($productAssociationType, $productsNames); |
|
|
|
|
698
|
|
|
} |
699
|
|
|
|
700
|
|
|
/** |
701
|
|
|
* @When I remove an associated product :productName from :productAssociationType |
702
|
|
|
*/ |
703
|
|
|
public function iRemoveAnAssociatedProductFromProductAssociation( |
704
|
|
|
$productName, |
705
|
|
|
ProductAssociationTypeInterface $productAssociationType |
706
|
|
|
) { |
707
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
708
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
709
|
|
|
$this->createSimpleProductPage, |
710
|
|
|
$this->updateSimpleProductPage, |
711
|
|
|
], $this->sharedStorage->get('product')); |
712
|
|
|
|
713
|
|
|
$currentPage->removeAssociatedProduct($productName, $productAssociationType); |
|
|
|
|
714
|
|
|
} |
715
|
|
|
|
716
|
|
|
/** |
717
|
|
|
* @Then /^(this product) should have(?:| also) an image with a code "([^"]*)"$/ |
718
|
|
|
* @Then /^the (product "[^"]+") should have(?:| also) an image with a code "([^"]*)"$/ |
719
|
|
|
*/ |
720
|
|
|
public function thisProductShouldHaveAnImageWithCode(ProductInterface $product, $code) |
721
|
|
|
{ |
722
|
|
|
$this->sharedStorage->set('product', $product); |
723
|
|
|
|
724
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
725
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
726
|
|
|
$this->updateSimpleProductPage, |
727
|
|
|
$this->updateConfigurableProductPage, |
728
|
|
|
], $product); |
729
|
|
|
|
730
|
|
|
Assert::true( |
731
|
|
|
$currentPage->isImageWithCodeDisplayed($code), |
732
|
|
|
sprintf('Image with a code %s should have been displayed.', $code) |
733
|
|
|
); |
734
|
|
|
} |
735
|
|
|
|
736
|
|
|
/** |
737
|
|
|
* @Then /^(this product) should not have(?:| also) an image with a code "([^"]*)"$/ |
738
|
|
|
*/ |
739
|
|
|
public function thisProductShouldNotHaveAnImageWithCode(ProductInterface $product, $code) |
740
|
|
|
{ |
741
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
742
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
743
|
|
|
$this->updateSimpleProductPage, |
744
|
|
|
$this->updateConfigurableProductPage, |
745
|
|
|
], $product); |
746
|
|
|
|
747
|
|
|
Assert::false( |
748
|
|
|
$currentPage->isImageWithCodeDisplayed($code), |
749
|
|
|
sprintf('Image with a code %s should not have been displayed.', $code) |
750
|
|
|
); |
751
|
|
|
} |
752
|
|
|
|
753
|
|
|
/** |
754
|
|
|
* @When I change the image with the :code code to :path |
755
|
|
|
*/ |
756
|
|
|
public function iChangeItsImageToPathForTheCode($path, $code) |
757
|
|
|
{ |
758
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
759
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
760
|
|
|
$this->updateSimpleProductPage, |
761
|
|
|
$this->updateConfigurableProductPage, |
762
|
|
|
], $this->sharedStorage->get('product')); |
763
|
|
|
|
764
|
|
|
$currentPage->changeImageWithCode($code, $path); |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
/** |
768
|
|
|
* @When /^I remove(?:| also) an image with a code "([^"]*)"$/ |
769
|
|
|
*/ |
770
|
|
|
public function iRemoveAnImageWithACode($code) |
771
|
|
|
{ |
772
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
773
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
774
|
|
|
$this->updateSimpleProductPage, |
775
|
|
|
$this->updateConfigurableProductPage, |
776
|
|
|
], $this->sharedStorage->get('product')); |
777
|
|
|
|
778
|
|
|
$currentPage->removeImageWithCode($code); |
779
|
|
|
} |
780
|
|
|
|
781
|
|
|
/** |
782
|
|
|
* @When I remove the first image |
783
|
|
|
*/ |
784
|
|
|
public function iRemoveTheFirstImage() |
785
|
|
|
{ |
786
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
787
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
788
|
|
|
$this->updateSimpleProductPage, |
789
|
|
|
$this->updateConfigurableProductPage, |
790
|
|
|
], $this->sharedStorage->get('product')); |
791
|
|
|
|
792
|
|
|
$currentPage->removeFirstImage(); |
793
|
|
|
} |
794
|
|
|
|
795
|
|
|
/** |
796
|
|
|
* @Then /^(this product) should not have any images$/ |
797
|
|
|
*/ |
798
|
|
|
public function thisProductShouldNotHaveImages(ProductInterface $product) |
799
|
|
|
{ |
800
|
|
|
$this->iWantToModifyAProduct($product); |
801
|
|
|
|
802
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
803
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
804
|
|
|
$this->updateSimpleProductPage, |
805
|
|
|
$this->updateConfigurableProductPage, |
806
|
|
|
], $this->sharedStorage->get('product')); |
807
|
|
|
|
808
|
|
|
Assert::same( |
809
|
|
|
0, |
810
|
|
|
$currentPage->countImages(), |
811
|
|
|
'This product has %2$s, but it should not have.' |
812
|
|
|
); |
813
|
|
|
} |
814
|
|
|
|
815
|
|
|
/** |
816
|
|
|
* @Then the image code field should be disabled |
817
|
|
|
*/ |
818
|
|
|
public function theImageCodeFieldShouldBeDisabled() |
819
|
|
|
{ |
820
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
821
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
822
|
|
|
$this->updateSimpleProductPage, |
823
|
|
|
$this->updateConfigurableProductPage, |
824
|
|
|
], $this->sharedStorage->get('product')); |
825
|
|
|
|
826
|
|
|
Assert::true( |
827
|
|
|
$currentPage->isImageCodeDisabled(), |
828
|
|
|
'Image code field should be disabled but it is not.' |
829
|
|
|
); |
830
|
|
|
} |
831
|
|
|
|
832
|
|
|
/** |
833
|
|
|
* @Then I should be notified that the image with this code already exists |
834
|
|
|
*/ |
835
|
|
|
public function iShouldBeNotifiedThatTheImageWithThisCodeAlreadyExists() |
836
|
|
|
{ |
837
|
|
|
Assert::same($this->updateSimpleProductPage->getValidationMessageForImage('code'), 'Image code must be unique within this product.'); |
|
|
|
|
838
|
|
|
} |
839
|
|
|
|
840
|
|
|
/** |
841
|
|
|
* @Then I should be notified that an image code is required |
842
|
|
|
*/ |
843
|
|
|
public function iShouldBeNotifiedThatAnImageCodeIsRequired() |
844
|
|
|
{ |
845
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
846
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
847
|
|
|
$this->updateSimpleProductPage, |
848
|
|
|
$this->updateConfigurableProductPage, |
849
|
|
|
], $this->sharedStorage->get('product')); |
850
|
|
|
|
851
|
|
|
Assert::same( |
852
|
|
|
$currentPage->getValidationMessageForImage(), |
853
|
|
|
'Please enter an image code.' |
854
|
|
|
); |
855
|
|
|
} |
856
|
|
|
|
857
|
|
|
/** |
858
|
|
|
* @Then there should still be only one image in the :product product |
859
|
|
|
*/ |
860
|
|
|
public function thereShouldStillBeOnlyOneImageInThisTaxon(ProductInterface $product) |
861
|
|
|
{ |
862
|
|
|
$this->iWantToModifyAProduct($product); |
863
|
|
|
|
864
|
|
|
/** @var UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface $currentPage */ |
865
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
866
|
|
|
$this->updateSimpleProductPage, |
867
|
|
|
$this->updateConfigurableProductPage, |
868
|
|
|
], $product); |
869
|
|
|
|
870
|
|
|
Assert::same( |
871
|
|
|
1, |
872
|
|
|
$currentPage->countImages(), |
873
|
|
|
'This product has %2$s images, but it should have only one.' |
874
|
|
|
); |
875
|
|
|
} |
876
|
|
|
|
877
|
|
|
/** |
878
|
|
|
* @Then /^there should be no reviews of (this product)$/ |
879
|
|
|
*/ |
880
|
|
|
public function thereAreNoProductReviews(ProductInterface $product) |
881
|
|
|
{ |
882
|
|
|
$this->productReviewIndexPage->open(); |
883
|
|
|
|
884
|
|
|
Assert::false( |
885
|
|
|
$this->productReviewIndexPage->isSingleResourceOnPage(['reviewSubject' => $product->getName()]), |
886
|
|
|
sprintf('There should be no reviews of %s.', $product->getName()) |
887
|
|
|
); |
888
|
|
|
} |
889
|
|
|
|
890
|
|
|
/** |
891
|
|
|
* @Then /^the product for ("[^"]+" currency) and ("[^"]+" channel) should have "(?:€|£|\$)([^"]+)"$/ |
892
|
|
|
*/ |
893
|
|
|
public function theProductForCurrencyAndChannelShouldHave(CurrencyInterface $currency, ChannelInterface $channel, $price) |
894
|
|
|
{ |
895
|
|
|
Assert::same( |
896
|
|
|
$this->updateSimpleProductPage->getPricingConfigurationForChannelAndCurrencyCalculator($channel, $currency), |
897
|
|
|
$price |
898
|
|
|
); |
899
|
|
|
} |
900
|
|
|
|
901
|
|
|
/** |
902
|
|
|
* @Then this product should( also) have an association :productAssociationType with product :productName |
903
|
|
|
* @Then this product should( also) have an association :productAssociationType with products :firstProductName and :secondProductName |
904
|
|
|
*/ |
905
|
|
|
public function theProductShouldHaveAnAssociationWithProducts( |
906
|
|
|
ProductAssociationTypeInterface $productAssociationType, |
907
|
|
|
...$productsNames |
908
|
|
|
) { |
909
|
|
|
foreach ($productsNames as $productName) { |
910
|
|
|
Assert::true( |
911
|
|
|
$this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType), |
912
|
|
|
sprintf( |
913
|
|
|
'This product should have an association %s with product %s, but it does not.', |
914
|
|
|
$productAssociationType->getName(), |
915
|
|
|
$productName |
916
|
|
|
) |
917
|
|
|
); |
918
|
|
|
} |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
/** |
922
|
|
|
* @Then this product should not have an association :productAssociationType with product :productName |
923
|
|
|
*/ |
924
|
|
|
public function theProductShouldNotHaveAnAssociationWithProducts( |
925
|
|
|
ProductAssociationTypeInterface $productAssociationType, |
926
|
|
|
$productName |
927
|
|
|
) { |
928
|
|
|
Assert::false( |
929
|
|
|
$this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType), |
930
|
|
|
sprintf( |
931
|
|
|
'This product should not have an association %s with product %s, but it does.', |
932
|
|
|
$productAssociationType->getName(), |
933
|
|
|
$productName |
934
|
|
|
) |
935
|
|
|
); |
936
|
|
|
} |
937
|
|
|
|
938
|
|
|
/** |
939
|
|
|
* @param string $element |
940
|
|
|
* @param string $value |
941
|
|
|
*/ |
942
|
|
|
private function assertElementValue($element, $value) |
943
|
|
|
{ |
944
|
|
|
/** @var UpdatePageInterface $currentPage */ |
945
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
946
|
|
|
$this->updateSimpleProductPage, |
947
|
|
|
$this->updateConfigurableProductPage, |
948
|
|
|
], $this->sharedStorage->get('product')); |
949
|
|
|
|
950
|
|
|
Assert::isInstanceOf($currentPage, UpdatePageInterface::class); |
951
|
|
|
|
952
|
|
|
Assert::true( |
953
|
|
|
$currentPage->hasResourceValues( |
954
|
|
|
[$element => $value] |
955
|
|
|
), |
956
|
|
|
sprintf('Product should have %s with %s value.', $element, $value) |
957
|
|
|
); |
958
|
|
|
} |
959
|
|
|
|
960
|
|
|
/** |
961
|
|
|
* @param string $element |
962
|
|
|
* @param string $message |
963
|
|
|
*/ |
964
|
|
|
private function assertValidationMessage($element, $message) |
965
|
|
|
{ |
966
|
|
|
$product = $this->sharedStorage->has('product') ? $this->sharedStorage->get('product') : null; |
967
|
|
|
|
968
|
|
|
/** @var CreatePageInterface|UpdatePageInterface $currentPage */ |
969
|
|
|
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([ |
970
|
|
|
$this->createSimpleProductPage, |
971
|
|
|
$this->createConfigurableProductPage, |
972
|
|
|
$this->updateSimpleProductPage, |
973
|
|
|
$this->updateConfigurableProductPage, |
974
|
|
|
], $product); |
975
|
|
|
|
976
|
|
|
Assert::same($currentPage->getValidationMessage($element), $message); |
977
|
|
|
} |
978
|
|
|
} |
979
|
|
|
|
PHP provides two ways to mark string literals. Either with single quotes
'literal'
or with double quotes"literal"
. The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (
\'
) and the backslash (\\
). Every other character is displayed as is.Double quoted string literals may contain other variables or more complex escape sequences.
will print an indented:
Single is Value
If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.
For more information on PHP string literals and available escape sequences see the PHP core documentation.