Merge branch '2.0' into api-platform-3

* 2.0: (30 commits)
  [CS][DX] Refactor
  [CS][DX] Refactor
  Fix taxon tree dropdown
  [Behat] Remove unnecessary step
  [Behat][Product] Fix and enable adding product with multiple attributes scenario
  [Admin][Product] Various scenarios fixes
  More fixes
  Minor fixes
  [Admin] Add validation for ratio value
  [Admin] Twig hooks, refactor
  [Admin] Refactor context
  [Admin] Enable behats
  [ProductOption][Behat] Use product option from sharedStorage
  Fix behats step
  Remove unnecessary lines
  [ProductOption][Behat] minor improvements
  [Admin][ProductOption][Behat] Change localeCode to locale_code
  [Admin][ProductOption] Add behat's test for "apply to all" functionality
  [Admin][Product] Change using live action to new way
  [Admin][ProductOption] Add apply to all button
  ...
This commit is contained in:
Grzegorz Sadowski 2024-07-16 07:10:57 +02:00
commit 8ad643977d
No known key found for this signature in database
GPG key ID: EF87FF4E6E3BD364
94 changed files with 637 additions and 611 deletions

View file

@ -8,12 +8,12 @@ Feature: Adding a new exchange rate
Given the store has currency "US Dollar" and "British Pound"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Adding a new exchange rate
When I want to add a new exchange rate
And I specify its ratio as 1.20
And I specify its ratio as 1.2
And I choose "US Dollar" as the source currency
And I choose "British Pound" as the target currency
And I add it
Then I should be notified that it has been successfully created
And the exchange rate with ratio 1.20000 between "US Dollar" and "British Pound" should appear in the store
And the exchange rate with ratio 1.2 between "US Dollar" and "British Pound" should appear in the store

View file

@ -10,7 +10,7 @@ Feature: Browsing exchange rates
And the exchange rate of "British Pound" to "Bhutanese Ngultrum" is 2.37
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Browsing store's exchange rates
When I am browsing exchange rates of the store
Then I should see 2 exchange rates on the list

View file

@ -8,7 +8,7 @@ Feature: Deleting exchange rates
Given the store has currency "US Dollar", "British Pound" and "Bhutanese Ngultrum"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Deleted exchange rate should disappear from the list
Given the exchange rate of "US Dollar" to "British Pound" is 1.2
When I delete the exchange rate between "US Dollar" and "British Pound"

View file

@ -11,7 +11,7 @@ Feature: Deleting multiple exchange rates
And the exchange rate of "Polish Zloty" to "Euro" is 0.22
And I am logged in as an administrator
@todo @ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Deleting multiple exchange rates at once
When I browse exchange rates
And I check the exchange rate between "Euro" and "British Pound"

View file

@ -8,7 +8,7 @@ Feature: Editing exchange rate
Given the store has currency "US Dollar" and "British Pound"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Change exchange rate's ratio
Given the exchange rate of "US Dollar" to "British Pound" is 1.30
And I am editing this exchange rate
@ -17,7 +17,7 @@ Feature: Editing exchange rate
Then I should be notified that it has been successfully edited
And it should have a ratio of 3.21
@todo @ui @api
@api @ui
Scenario: Being unable to change currencies
Given the exchange rate of "US Dollar" to "British Pound" is 1.30
When I want to edit this exchange rate

View file

@ -8,7 +8,7 @@ Feature: Exchange rate validation
Given the store has currency "US Dollar" and "British Pound"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Trying to add a new exchange rate without ratio
When I want to add a new exchange rate
And I choose "US Dollar" as the source currency
@ -18,7 +18,7 @@ Feature: Exchange rate validation
Then I should be notified that ratio is required
And the exchange rate between "US Dollar" and "British Pound" should not be added
@todo @ui @api
@api @ui
Scenario: Trying to add a new exchange rate with negative ratio
When I want to add a new exchange rate
And I choose "US Dollar" as the source currency
@ -28,7 +28,27 @@ Feature: Exchange rate validation
Then I should be notified that the ratio must be greater than zero
And the exchange rate between "US Dollar" and "British Pound" should not be added
@todo @ui @api
@api @ui
Scenario: Trying to add a new exchange rate with an excessively high ratio
When I want to add a new exchange rate
And I choose "US Dollar" as the source currency
And I choose "British Pound" as the target currency
And I specify its ratio as 123450000
And I try to add it
Then I should be notified that the ratio must be less than 100000
And the exchange rate between "US Dollar" and "British Pound" should not be added
@api @ui
Scenario: Trying to add a new exchange rate with an zero ratio
When I want to add a new exchange rate
And I choose "US Dollar" as the source currency
And I choose "British Pound" as the target currency
And I specify its ratio as 0
And I try to add it
Then I should be notified that the ratio must be greater than zero
And the exchange rate between "US Dollar" and "British Pound" should not be added
@api @ui
Scenario: Trying to add a new exchange rate with same target currency as source
When I want to add a new exchange rate
And I specify its ratio as 1.23

View file

@ -11,7 +11,7 @@ Feature: Filtering exchange rates by a currency
And the exchange rate of "Polish Zloty" to "Euro" is 0.22
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Filtering exchange rates by a chosen currency
When I browse exchange rates of the store
And I choose "Euro" as a currency filter

View file

@ -9,7 +9,7 @@ Feature: Inability of adding exchange rates with the same currency pair
And the exchange rate of "Euro" to "British Pound" is 1.2
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Being prevented from adding an exchange rate for the same currency pair
When I want to add a new exchange rate
And I specify its ratio as 3.20
@ -20,7 +20,7 @@ Feature: Inability of adding exchange rates with the same currency pair
And I should still see one exchange rate on the list
And this exchange rate should have a ratio of 1.2
@todo @ui @api
@api @ui
Scenario: Being prevented from adding an exchange rate for a reversed currency pair
When I want to add a new exchange rate
And I specify its ratio as 3.20

View file

@ -8,7 +8,7 @@ Feature: Adding a new product option
Given the store is available in "English (United States)"
And I am logged in as an administrator
@ui @javascript @todo-api
@todo-api @ui @javascript
Scenario: Adding a new product option with two required option values
When I want to create a new product option
And I name it "T-Shirt size" in "English (United States)"
@ -21,7 +21,7 @@ Feature: Adding a new product option
And product option "T-Shirt size" should have the "S" option value
And product option "T-Shirt size" should have the "M" option value
@ui @api
@api @ui
Scenario: Adding a new product option without any option values
When I want to create a new product option
And I name it "T-Shirt size" in "English (United States)"
@ -31,7 +31,7 @@ Feature: Adding a new product option
Then I should be notified that it has been successfully created
And the product option "T-Shirt size" should appear in the registry
@ui @javascript @todo-api
@todo-api @ui @javascript
Scenario: Adding a new product option with one option value
When I want to create a new product option
And I name it "T-Shirt size" in "English (United States)"
@ -41,3 +41,20 @@ Feature: Adding a new product option
Then I should be notified that it has been successfully created
And the product option "T-Shirt size" should appear in the registry
And product option "T-Shirt size" should have the "S" option value
@no-api @ui @javascript
Scenario: Adding a new product option with applying option value to all option values
Given the store is also available in "Polish (Poland)"
When I want to create a new product option
And I name it "T-Shirt size" in "English (United States)"
And I specify its code as "t_shirt_size"
And I add the "S" option value identified by "OV1" in "English (United States)"
And I apply the option value identified by 'OV1' in 'English (United States)' to all option values.
And I add the "M" option value identified by "OV2" in "English (United States)"
And I add it
Then I should be notified that it has been successfully created
And the product option "T-Shirt size" should appear in the registry
And this product option should have the "S" option value in "English (United States)" locale
And this product option should have the "S" option value in "Polish (Poland)" locale
And this product option should have the "M" option value in "English (United States)" locale
But this product option should not have the "M" option value in "Polish (Poland)" locale

View file

@ -4,7 +4,7 @@ Feature: Browsing product options
As an Administrator
I want to be able to browse product options
@ui @api
@api @ui
Scenario: Browsing defined product options
Given I am logged in as an administrator
And the store has a product option "T-Shirt size" with a code "t_shirt_size"

View file

@ -10,7 +10,7 @@ Feature: Deleting multiple product options
And the store has also a product option "T-Shirt brand"
And I am logged in as an administrator
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Deleting multiple product options at once
When I browse product options
And I check the "T-Shirt size" product option

View file

@ -9,7 +9,7 @@ Feature: Editing product options
And the store has a product option "T-Shirt size" with a code "t_shirt_size"
And I am logged in as an administrator
@ui @todo-api
@todo-api @ui
Scenario: Renaming the product option
Given this product option has the "S" option value with code "t_shirt_size_s"
And this product option has also the "M" option value with code "t_shirt_size_m"

View file

@ -11,14 +11,14 @@ Feature: Filtering product options
And I am logged in as an administrator
And I am browsing product options
@ui @todo-api
@todo-api @ui
Scenario: Filtering product options by name
When I search for product options with "T-Shirt"
Then I should see 2 product options in the list
And the product option "T-Shirt size" should be in the registry
And the product option "T-Shirt color" should be in the registry
@ui @todo-api
@todo-api @ui
Scenario: Filtering product options by code
When I search for product options with "xyz"
Then I should see 2 product options in the list

View file

@ -11,7 +11,7 @@ Feature: Managing option values of a product option
And this product option has also the "M" option value with code "OV2"
And I am logged in as an administrator
@ui @mink:chromedriver @todo-api
@todo-api @ui @javascript
Scenario: Adding an option value to an existing product option
When I want to modify the "T-Shirt size" product option
And I add the "L" option value identified by "OV3"

View file

@ -9,7 +9,7 @@ Feature: Product option unique code validation
And the store has a product option "T-Shirt size" with a code "t_shirt_size"
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Trying to add product option with a taken code
When I want to create a new product option
And I name it "T-Shirt color" in "English (United States)"

View file

@ -9,7 +9,7 @@ Feature: Product option validation
And the store has a product option "T-Shirt color" with a code "t_shirt_color"
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Trying to add a new product option without specifying its code
When I want to create a new product option
And I name it "T-Shirt size" in "English (United States)"
@ -18,14 +18,14 @@ Feature: Product option validation
Then I should be notified that code is required
And the product option with name "T-Shirt size" should not be added
@no-ui @api
@api @no-ui
Scenario: Trying to add a new product option translation in unexsting locale
When I want to modify the "T-Shirt color" product option
And I name it "T-Shirt color" in "French (France)"
And I try to save my changes
Then I should be notified that the locale is not available
@ui @api
@api @ui
Scenario: Trying to add a new product option with a too long code
When I want to create a new product option
And I name it "T-Shirt size" in "English (United States)"
@ -33,7 +33,7 @@ Feature: Product option validation
And I try to add it
Then I should be notified that code is too long
@ui @api
@api @ui
Scenario: Trying to add a new product option without specifying its name
When I want to create a new product option
And I specify its code as "t_shirt_size"
@ -42,7 +42,7 @@ Feature: Product option validation
Then I should be notified that name is required
And the product option with code "t_shirt_size" should not be added
@ui @api
@api @ui
Scenario: Trying to remove name from an existing product option
When I want to modify the "T-Shirt color" product option
And I remove its name from "English (United States)" translation

View file

@ -11,7 +11,7 @@ Feature: Product option values translation validation
And this product option has also the "M" option value with code "OV2"
And I am logged in as an administrator
@no-ui @api
@api @no-ui
Scenario: Trying to add product option value translation in unexisting locale
When I want to modify the "T-Shirt size" product option
And I add the "X" option value identified by "OV3" in "French (France)"

View file

@ -10,20 +10,20 @@ Feature: Sorting listed product options by position
And the store has also a product option "Mug type" at position 1
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Product options are sorted by position in ascending order by default
When I browse product options
Then I should see 3 product options in the list
And the first product option in the list should have name "Mug size"
And the last product option in the list should have name "Mug color"
@ui @api
@api @ui
Scenario: Product option added at no position is added as the last one
Given the store has a product option "Mug shape"
When I browse product options
Then the last product option in the list should have name "Mug shape"
@ui @api
@api @ui
Scenario: Product option added at position 0 is added as the first one
Given the store has a product option "Mug shape" at position 0
When I browse product options

View file

@ -10,7 +10,7 @@ Feature: Accessing a store's product page
And I am logged in as an administrator
And I am using "Polish (Poland)" locale for my panel
@todo @ui @no-api
@no-api @ui
Scenario: Accessing the product shop page from the admin panel when the product has a translation with a defined slug in the administrator's chosen language
Given the locale "Polish (Poland)" is enabled
And this product is named "Bulldog francuski T-Shirt" in the "Polish (Poland)" locale
@ -18,7 +18,7 @@ Feature: Accessing a store's product page
Then the show product's page button should be enabled
And it should be leading to the product's page in the "Polish (Poland)" locale
@todo @ui @no-api
@no-api @ui
Scenario: Accessing the product shop page from the admin panel when the product has a translation with a defined slug in the default channel's language
Given the locale "French (France)" is enabled
And this product is named "Tee-shirt bouledogue français" in the "French (France)" locale
@ -26,7 +26,7 @@ Feature: Accessing a store's product page
Then the show product's page button should be enabled
And it should be leading to the product's page in the "German (Germany)" locale
@todo @ui @no-api
@no-api @ui
Scenario: Accessing the product shop page from the admin panel with using first available locale with slug and enabled in the channel
Given the locale "French (France)" is enabled
And the store also operates in "French (France)" locale
@ -36,7 +36,7 @@ Feature: Accessing a store's product page
Then the show product's page button should be enabled
And it should be leading to the product's page in the "French (France)" locale
@todo @ui @no-api
@no-api @ui
Scenario: Not being able to access the product shop page from the admin panel when the product has no translations meeting the criteria
Given this product has no translations with a defined slug
When I want to edit this product

View file

@ -10,19 +10,19 @@ Feature: Accessing the variants management from the product edit page
And this product has option "Model" with values "RS6" and "RS7"
And I am logged in as an administrator
@todo @ui @no-api
@no-api @ui
Scenario: Being able to access the variants list page
When I modify the "Audi" product
And I go to the variants list
Then I should be on the list of this product's variants
@todo @ui @no-api
@no-api @ui
Scenario: Being able to access the variant creation page
When I modify the "Audi" product
And I go to the variant creation page
Then I should be on the variant creation page for this product
@todo @ui @no-api
@no-api @ui
Scenario: Being able to access the variant generation page
When I modify the "Audi" product
And I go to the variant generation page

View file

@ -10,11 +10,14 @@ Feature: Adding a new integer product attribute
And the store has a non-translatable text product attribute "ISBN"
And I am logged in as an administrator
@todo @ui @javascript @api
@api @ui @mink:chromedriver
Scenario: Adding two text attributes to a product
When I want to create a new configurable product
And I specify its code as "HARRY_POTTER_1"
And I name it "Harry Potter and the Sorcerer's Stone" in "English (United States)"
And I name it "Harry Potter and the Sorcerer's Stone" in "English (United States)" locale
And I generate its slug in "English (United States)" locale
And I add the "Author" attribute
And I add the "ISBN" attribute
And I set its non-translatable "Author" attribute to "J.K. Rowling"
And I set its non-translatable "ISBN" attribute to "978-1338878929"
And I add it

View file

@ -8,12 +8,12 @@ Feature: Adding a product with only original price
Given the store operates on a single channel in "United States"
And I am logged in as an administrator
@todo @ui @no-api
@no-api @ui
Scenario: Adding a new simple product without price
When I want to create a new simple product
And I specify its code as "BOARD_DICE_BREWING"
And I name it "Dice Brewing" in "English (United States)"
And I set its slug to "games/Dice-brewing" in "English (United States)"
And I name it "Dice Brewing" in "English (United States)" locale
And I set its slug to "games/Dice-brewing" in "English (United States)" locale
And I set its original price to "$100.00" for "United States" channel
And I add it
Then I should be notified that it has been successfully created

View file

@ -10,11 +10,11 @@ Feature: Adding product with prices for multiple channels
And the store operates on another channel named "Web-GB" in "GBP" currency
And I am logged in as an administrator
@todo @ui @no-api
@no-api @ui
Scenario: Configure prices for each channel and currency while adding a new simple product
When I want to create a new simple product
And I specify its code as "BOARD_DICE_BREWING"
And I name it "Dice Brewing" in "English (United States)"
And I name it "Dice Brewing" in "English (United States)" locale
And I set its price to "$10.00" for "Web-US" channel
And I set its price to "£5.00" for "Web-GB" channel
And I set its slug to "dice-brewing"
@ -23,21 +23,21 @@ Feature: Adding product with prices for multiple channels
And product "Dice Brewing" should be priced at $10.00 for channel "Web-US"
And product "Dice Brewing" should be priced at £5.00 for channel "Web-GB"
@todo @ui @no-api
@no-api @ui
Scenario: Configure prices for each channel while adding a new simple product
When I want to create a new simple product
And I specify its code as "BOARD_DICE_BREWING"
And I name it "Dice Brewing" in "English (United States)"
And I name it "Dice Brewing" in "English (United States)" locale
And I make it available in channel "Web-US"
And I set its slug to "dice-brewing"
And I add it
Then I should be notified that price must be defined for every channel
@todo @ui @no-api
@no-api @ui
Scenario: Require prices only for chosen channels
When I want to create a new simple product
And I specify its code as "BOARD_DICE_BREWING"
And I name it "Dice Brewing" in "English (United States)"
And I name it "Dice Brewing" in "English (United States)" locale
And I make it available in channel "Web-US"
And I set its price to "$10.00" for "Web-US" channel
And I set its slug to "dice-brewing"
@ -45,11 +45,11 @@ Feature: Adding product with prices for multiple channels
Then I should be notified that it has been successfully created
And product "Dice Brewing" should be priced at $10.00 for channel "Web-US"
@todo @ui @no-api
@no-api @ui
Scenario: Require prices for all chosen channels
When I want to create a new simple product
And I specify its code as "BOARD_DICE_BREWING"
And I name it "Dice Brewing" in "English (United States)"
And I name it "Dice Brewing" in "English (United States)" locale
And I make it available in channel "Web-US"
And I make it available in channel "Web-GB"
And I set its price to "$10.00" for "Web-US" channel
@ -57,11 +57,11 @@ Feature: Adding product with prices for multiple channels
And I add it
Then I should be notified that price must be defined for every channel
@todo @ui @no-api
@no-api @ui
Scenario: Do not specify price while adding a new simple product
When I want to create a new simple product
And I specify its code as "BOARD_DICE_BREWING"
And I name it "Dice Brewing" in "English (United States)"
And I name it "Dice Brewing" in "English (United States)" locale
And I set its slug to "dice-brewing"
And I add it
Then I should be notified that it has been successfully created

View file

@ -8,7 +8,7 @@ Feature: Deleting multiple products
Given the store has "Audi RS5 model", "Audi RS6 model" and "Audi RS7 model" products
And I am logged in as an administrator
@todo @ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Deleting multiple products at once
When I browse products
And I check the "Audi RS5 model" product

View file

@ -10,12 +10,12 @@ Feature: Preventing a potential XSS attack while adding a new product
And the store has "No XSS" taxonomy
And I am logged in as an administrator
@ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Preventing a potential XSS attack while adding new product
When I want to create a new simple product
Then I should be able to name it "No XSS" in "English (United States)" locale
@todo @ui @javascript @no-api
@no-api @ui @mink:chromedriver
Scenario: Preventing a potential XSS attack while choosing main taxon for a new product
When I want to create a new simple product
Then I should be able to choose main taxon "No XSS"

View file

@ -12,7 +12,7 @@ Feature: Removing a product's price after channel deletion
And this product is also priced at "£5.00" in "Web-GB" channel
And I am logged in as an administrator
@todo @ui @no-api
@no-api @ui
Scenario: Removing a product's price after corresponding channel deletion
When channel "Web-GB" has been deleted
Then product "Dice Brewing" should be priced at $10.00 for channel "Web-US"

View file

@ -13,7 +13,7 @@ Feature: Removing a product's price from the channel where it is not available i
And this product is disabled in "Web-GB" channel
And I am logged in as an administrator
@todo @ui @no-api
@no-api @ui
Scenario: Removing a product's price from disabled channel
Given the channel "Web-GB" has been disabled
When I want to modify the "Dice Brewing" product
@ -22,7 +22,7 @@ Feature: Removing a product's price from the channel where it is not available i
Then I should not have configured price for "Web-GB" channel
But I should have original price equal to "£70.00" in "Web-GB" channel
@todo @ui @no-api
@no-api @ui
Scenario: Removing a product's price from obsolete channel
Given this product is disabled in "Web-GB" channel
When I want to modify the "Dice Brewing" product

View file

@ -8,7 +8,7 @@ Feature: Toggling the inventory tracking
Given the store has a product "Dice Brewing"
And I am logged in as an administrator
@todo @ui @no-api
@no-api @ui
Scenario: Disabling inventory for a simple product
Given the "Dice Brewing" product is tracked by the inventory
When I want to modify the "Dice Brewing" product
@ -17,7 +17,7 @@ Feature: Toggling the inventory tracking
Then I should be notified that it has been successfully edited
And inventory of this product should not be tracked
@todo @ui @no-api
@no-api @ui
Scenario: Enabling inventory for a simple product
When I want to modify the "Dice Brewing" product
And I enable its inventory tracking

View file

@ -1,5 +1,5 @@
@accessing_edit_page_from_product_show_page
Feature: Accessing to product edit page from show page
Feature: Accessing the product edit page from the show page
In order to edit product in the simple way
As an Administrator
I want to be able to move to edit page directly from product show page
@ -14,12 +14,12 @@ Feature: Accessing to product edit page from show page
@todo @ui @no-api
Scenario: Accessing to product edit page from product show page
When I access the "Iron Shield" product
When I access the "Iron shield" product
And I go to edit page
Then I should be on "Iron shield" product edit page
@todo @ui @no-api
Scenario: Accessing to variant edit page from product show page
When I access the "Iron Shield" product
When I access the "Iron shield" product
And I go to edit page of "Iron shield - very big" variant
Then I should be on "Iron shield - very big" variant edit page

View file

@ -1,24 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Behaviour;
trait ChoosesName
{
use DocumentAccessor;
public function chooseName(string $name): void
{
$this->getDocument()->selectFieldOption('Name', $name);
}
}

View file

@ -22,7 +22,7 @@ use Sylius\Component\Currency\Model\CurrencyInterface;
use Sylius\Component\Currency\Model\ExchangeRateInterface;
use Webmozart\Assert\Assert;
final class ManagingExchangeRatesContext implements Context
final readonly class ManagingExchangeRatesContext implements Context
{
public function __construct(
private ApiClientInterface $client,
@ -278,6 +278,17 @@ final class ManagingExchangeRatesContext implements Context
);
}
/**
* @Then I should be notified that the ratio must be less than :value
*/
public function iShouldBeNotifiedThatRatioMustBeLessThan(string $value): void
{
Assert::contains(
$this->responseChecker->getError($this->client->getLastResponse()),
sprintf('The ratio must be less than %s.', $value),
);
}
/**
* @Then I should be notified that source and target currencies must differ
*/

View file

@ -102,19 +102,9 @@ final class ManagingProductOptionsContext implements Context
/**
* @When I add the :value option value identified by :code
*/
public function iAddTheOptionValueWithCodeAndValue(string $value, string $code): void
{
$this->client->addSubResourceData(
'values',
['code' => $code, 'translations' => ['en_US' => ['value' => $value]]],
);
}
/**
* @When I add the :value option value identified by :code in :localeCode
*/
public function iAddTheOptionValueWithCodeAndValueInLocale(string $value, string $code, string $localeCode): void
public function iAddTheOptionValueWithCodeAndValue(string $value, string $code, string $localeCode = 'en_US'): void
{
$this->client->addSubResourceData(
'values',

View file

@ -56,7 +56,7 @@ final readonly class ManagingCustomerGroupsContext implements Context
* @When I specify its name as :name
* @When I remove its name
*/
public function iSpecifyItsNameAs(string $name = null): void
public function iSpecifyItsNameAs(?string $name = null): void
{
$this->createPage->nameIt($name ?? '');
}

View file

@ -14,26 +14,28 @@ declare(strict_types=1);
namespace Sylius\Behat\Context\Ui\Admin;
use Behat\Behat\Context\Context;
use Sylius\Behat\Page\Admin\ExchangeRate\CreatePageInterface;
use Sylius\Behat\Element\Admin\ExchangeRate\FormElementInterface;
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface;
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface;
use Sylius\Behat\Page\Admin\ExchangeRate\IndexPageInterface;
use Sylius\Behat\Page\Admin\ExchangeRate\UpdatePageInterface;
use Sylius\Component\Currency\Model\CurrencyInterface;
use Sylius\Component\Currency\Model\ExchangeRateInterface;
use Webmozart\Assert\Assert;
final class ManagingExchangeRatesContext implements Context
final readonly class ManagingExchangeRatesContext implements Context
{
public function __construct(
private CreatePageInterface $createPage,
private IndexPageInterface $indexPage,
private UpdatePageInterface $updatePage,
private FormElementInterface $formElement,
) {
}
/**
* @When I want to add a new exchange rate
*/
public function iWantToAddNewExchangeRate()
public function iWantToAddNewExchangeRate(): void
{
$this->createPage->open();
}
@ -42,7 +44,7 @@ final class ManagingExchangeRatesContext implements Context
* @When /^I want to edit (this exchange rate)$/
* @When /^I am editing (this exchange rate)$/
*/
public function iWantToEditThisExchangeRate(ExchangeRateInterface $exchangeRate)
public function iWantToEditThisExchangeRate(ExchangeRateInterface $exchangeRate): void
{
$this->updatePage->open(['id' => $exchangeRate->getId()]);
}
@ -52,7 +54,7 @@ final class ManagingExchangeRatesContext implements Context
* @When I browse exchange rates
* @When I browse exchange rates of the store
*/
public function iWantToBrowseExchangeRatesOfTheStore()
public function iWantToBrowseExchangeRatesOfTheStore(): void
{
$this->indexPage->open();
}
@ -61,31 +63,31 @@ final class ManagingExchangeRatesContext implements Context
* @When /^I specify its ratio as (-?[0-9\.]+)$/
* @When I don't specify its ratio
*/
public function iSpecifyItsRatioAs($ratio = null)
public function iSpecifyItsRatioAs(?string $ratio = null): void
{
$this->createPage->specifyRatio($ratio ?? '');
$this->formElement->specifyRatio($ratio ?? '');
}
/**
* @When I choose :currencyCode as the source currency
*/
public function iChooseAsSourceCurrency($currencyCode)
public function iChooseAsSourceCurrency(string $currencyCode): void
{
$this->createPage->chooseSourceCurrency($currencyCode);
$this->formElement->specifySourceCurrency($currencyCode);
}
/**
* @When I choose :currencyCode as the target currency
*/
public function iChooseAsTargetCurrency($currencyCode)
public function iChooseAsTargetCurrency(string $currencyCode): void
{
$this->createPage->chooseTargetCurrency($currencyCode);
$this->formElement->specifyTargetCurrency($currencyCode);
}
/**
* @When I( try to) add it
*/
public function iAddIt()
public function iAddIt(): void
{
$this->createPage->create();
}
@ -93,15 +95,15 @@ final class ManagingExchangeRatesContext implements Context
/**
* @When I change ratio to :ratio
*/
public function iChangeRatioTo($ratio)
public function iChangeRatioTo(string $ratio): void
{
$this->updatePage->changeRatio($ratio);
$this->formElement->specifyRatio($ratio);
}
/**
* @When I save my changes
*/
public function iSaveMyChanges()
public function iSaveMyChanges(): void
{
$this->updatePage->saveChanges();
}
@ -109,7 +111,7 @@ final class ManagingExchangeRatesContext implements Context
/**
* @When I delete the exchange rate between :sourceCurrencyName and :targetCurrencyName
*/
public function iDeleteTheExchangeRateBetweenAnd($sourceCurrencyName, $targetCurrencyName)
public function iDeleteTheExchangeRateBetweenAnd(string $sourceCurrencyName, string $targetCurrencyName): void
{
$this->indexPage->open();
@ -122,7 +124,7 @@ final class ManagingExchangeRatesContext implements Context
/**
* @When I choose :currencyName as a currency filter
*/
public function iChooseCurrencyAsACurrencyFilter($currencyName)
public function iChooseCurrencyAsACurrencyFilter(string $currencyName): void
{
$this->indexPage->chooseCurrencyFilter($currencyName);
}
@ -130,7 +132,7 @@ final class ManagingExchangeRatesContext implements Context
/**
* @When I filter
*/
public function iFilter()
public function iFilter(): void
{
$this->indexPage->filter();
}
@ -157,7 +159,7 @@ final class ManagingExchangeRatesContext implements Context
/**
* @Then I should see :count exchange rates on the list
*/
public function iShouldSeeExchangeRatesOnTheList($count = 0)
public function iShouldSeeExchangeRatesOnTheList(int $count): void
{
$this->assertCountOfExchangeRatesOnTheList($count);
}
@ -166,7 +168,7 @@ final class ManagingExchangeRatesContext implements Context
* @Then I should see a single exchange rate in the list
* @Then I should( still) see one exchange rate on the list
*/
public function iShouldSeeOneExchangeRateOnTheList()
public function iShouldSeeOneExchangeRateOnTheList(): void
{
$this->indexPage->open();
@ -176,11 +178,14 @@ final class ManagingExchangeRatesContext implements Context
/**
* @Then the exchange rate with ratio :ratio between :sourceCurrency and :targetCurrency should appear in the store
*/
public function theExchangeRateBetweenAndShouldAppearInTheStore($ratio, CurrencyInterface $sourceCurrency, CurrencyInterface $targetCurrency)
{
public function theExchangeRateBetweenAndShouldAppearInTheStore(
string $ratio,
CurrencyInterface $sourceCurrency,
CurrencyInterface $targetCurrency,
): void {
$this->indexPage->open();
$this->assertExchangeRateWithRatioIsOnTheList((float) $ratio, $sourceCurrency->getName(), $targetCurrency->getName());
$this->assertExchangeRateWithRatioIsOnTheList($ratio, $sourceCurrency->getName(), $targetCurrency->getName());
}
/**
@ -200,15 +205,15 @@ final class ManagingExchangeRatesContext implements Context
/**
* @Then it should have a ratio of :ratio
*/
public function thisExchangeRateShouldHaveRatioOf($ratio)
public function thisExchangeRateShouldHaveRatioOf(string $ratio): void
{
Assert::eq($this->updatePage->getRatio(), $ratio);
Assert::eq($this->formElement->getRatio(), $ratio);
}
/**
* @Then /^(this exchange rate) should no longer be on the list$/
*/
public function thisExchangeRateShouldNoLongerBeOnTheList(ExchangeRateInterface $exchangeRate)
public function thisExchangeRateShouldNoLongerBeOnTheList(ExchangeRateInterface $exchangeRate): void
{
$this->assertExchangeRateIsNotOnTheList(
$exchangeRate->getSourceCurrency()->getName(),
@ -219,7 +224,7 @@ final class ManagingExchangeRatesContext implements Context
/**
* @Then the exchange rate between :sourceCurrencyName and :targetCurrencyName should not be added
*/
public function theExchangeRateBetweenAndShouldNotBeAdded($sourceCurrencyName, $targetCurrencyName)
public function theExchangeRateBetweenAndShouldNotBeAdded(string $sourceCurrencyName, string $targetCurrencyName): void
{
$this->indexPage->open();
@ -229,7 +234,7 @@ final class ManagingExchangeRatesContext implements Context
/**
* @Then /^(this exchange rate) should have a ratio of ([0-9\.]+)$/
*/
public function thisExchangeRateShouldHaveARatioOf(ExchangeRateInterface $exchangeRate, $ratio)
public function thisExchangeRateShouldHaveARatioOf(ExchangeRateInterface $exchangeRate, string $ratio): void
{
$sourceCurrencyName = $exchangeRate->getSourceCurrency()->getName();
$targetCurrencyName = $exchangeRate->getTargetCurrency()->getName();
@ -242,7 +247,7 @@ final class ManagingExchangeRatesContext implements Context
*/
public function iShouldNotBeAbleToEditItsSourceCurrency(): void
{
Assert::true($this->updatePage->isSourceCurrencyDisabled());
Assert::true($this->formElement->isFieldDisabled('source_currency'));
}
/**
@ -250,16 +255,16 @@ final class ManagingExchangeRatesContext implements Context
*/
public function iShouldNotBeAbleToEditItsTargetCurrency(): void
{
Assert::true($this->updatePage->isTargetCurrencyDisabled());
Assert::true($this->formElement->isFieldDisabled('target_currency'));
}
/**
* @Then /^I should be notified that ([^"]+) is required$/
*/
public function iShouldBeNotifiedThatIsRequired($element)
public function iShouldBeNotifiedThatIsRequired(string $element): void
{
Assert::same(
$this->createPage->getValidationMessage($element),
$this->formElement->getValidationMessage($element),
sprintf('Please enter exchange rate %s.', $element),
);
}
@ -267,15 +272,23 @@ final class ManagingExchangeRatesContext implements Context
/**
* @Then I should be notified that the ratio must be greater than zero
*/
public function iShouldBeNotifiedThatRatioMustBeGreaterThanZero()
public function iShouldBeNotifiedThatRatioMustBeGreaterThanZero(): void
{
Assert::same($this->createPage->getValidationMessage('ratio'), 'The ratio must be greater than 0.');
Assert::same($this->formElement->getValidationMessage('ratio'), 'The ratio must be greater than 0.');
}
/**
* @Then I should be notified that the ratio must be less than :value
*/
public function iShouldBeNotifiedThatRatioMustBeLessThan(string $value): void
{
Assert::same($this->formElement->getValidationMessage('ratio'), sprintf('The ratio must be less than %s.', $value));
}
/**
* @Then I should be notified that source and target currencies must differ
*/
public function iShouldBeNotifiedThatSourceAndTargetCurrenciesMustDiffer()
public function iShouldBeNotifiedThatSourceAndTargetCurrenciesMustDiffer(): void
{
$this->assertFormHasValidationMessage('The source and target currencies must differ.');
}
@ -283,23 +296,22 @@ final class ManagingExchangeRatesContext implements Context
/**
* @Then I should be notified that the currency pair must be unique
*/
public function iShouldBeNotifiedThatTheCurrencyPairMustBeUnique()
public function iShouldBeNotifiedThatTheCurrencyPairMustBeUnique(): void
{
$this->assertFormHasValidationMessage('The currency pair must be unique.');
}
/**
* @param float $ratio
* @param string $sourceCurrencyName
* @param string $targetCurrencyName
*
* @throws \InvalidArgumentException
*/
private function assertExchangeRateWithRatioIsOnTheList($ratio, $sourceCurrencyName, $targetCurrencyName)
{
private function assertExchangeRateWithRatioIsOnTheList(
string $ratio,
string $sourceCurrencyName,
string $targetCurrencyName,
): void {
Assert::true(
$this->indexPage->isSingleResourceOnPage([
'ratio' => (string) $ratio,
'ratio' => $ratio,
'sourceCurrency' => $sourceCurrencyName,
'targetCurrency' => $targetCurrencyName,
]),
@ -313,12 +325,9 @@ final class ManagingExchangeRatesContext implements Context
}
/**
* @param string $sourceCurrencyName
* @param string $targetCurrencyName
*
* @throws \InvalidArgumentException
*/
private function assertExchangeRateIsNotOnTheList($sourceCurrencyName, $targetCurrencyName)
private function assertExchangeRateIsNotOnTheList(string $sourceCurrencyName, string $targetCurrencyName): void
{
Assert::false(
$this->indexPage->isSingleResourceOnPage([
@ -334,28 +343,24 @@ final class ManagingExchangeRatesContext implements Context
}
/**
* @param int $count
*
* @throws \InvalidArgumentException
*/
private function assertCountOfExchangeRatesOnTheList($count)
private function assertCountOfExchangeRatesOnTheList(int $count): void
{
Assert::same(
$this->indexPage->countItems(),
(int) $count,
$count,
'Expected %2$d exchange rates to be on the list, but found %d instead.',
);
}
/**
* @param string $expectedMessage
*
* @throws \InvalidArgumentException
*/
private function assertFormHasValidationMessage($expectedMessage)
private function assertFormHasValidationMessage(string $expectedMessage): void
{
Assert::true(
$this->createPage->hasFormValidationError($expectedMessage),
$this->formElement->hasFormValidationError($expectedMessage),
sprintf(
'The validation message "%s" was not found on the page.',
$expectedMessage,

View file

@ -18,24 +18,26 @@ use Sylius\Behat\Element\Admin\ProductOption\FormElementInterface;
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface;
use Sylius\Behat\Page\Admin\Crud\IndexPageInterface;
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Formatter\StringInflector;
use Sylius\Component\Product\Model\ProductOptionInterface;
use Webmozart\Assert\Assert;
final class ManagingProductOptionsContext implements Context
final readonly class ManagingProductOptionsContext implements Context
{
public function __construct(
private IndexPageInterface $indexPage,
private CreatePageInterface $createPage,
private UpdatePageInterface $updatePage,
private FormElementInterface $formElement,
private SharedStorageInterface $sharedStorage,
) {
}
/**
* @When I want to create a new product option
*/
public function iWantToCreateANewProductOption()
public function iWantToCreateANewProductOption(): void
{
$this->createPage->open();
}
@ -43,9 +45,11 @@ final class ManagingProductOptionsContext implements Context
/**
* @When I want to modify the :productOption product option
*/
public function iWantToModifyAProductOption(ProductOptionInterface $productOption)
public function iWantToModifyAProductOption(ProductOptionInterface $productOption): void
{
$this->updatePage->open(['id' => $productOption->getId()]);
if (!$this->updatePage->isOpen(['id' => $productOption->getId()])) {
$this->updatePage->open(['id' => $productOption->getId()]);
}
}
/**
@ -60,7 +64,7 @@ final class ManagingProductOptionsContext implements Context
* @Given I am browsing product options
* @When I browse product options
*/
public function iBrowseProductOptions()
public function iBrowseProductOptions(): void
{
$this->indexPage->open();
}
@ -69,7 +73,7 @@ final class ManagingProductOptionsContext implements Context
* @When I add it
* @When I try to add it
*/
public function iAddIt()
public function iAddIt(): void
{
$this->createPage->create();
}
@ -78,7 +82,7 @@ final class ManagingProductOptionsContext implements Context
* @When I save my changes
* @When I try to save my changes
*/
public function iSaveMyChanges()
public function iSaveMyChanges(): void
{
$this->updatePage->saveChanges();
}
@ -86,9 +90,9 @@ final class ManagingProductOptionsContext implements Context
/**
* @When I name it :name in :language
*/
public function iNameItInLanguage($name, $language)
public function iNameItInLanguage($name, $language): void
{
$this->formElement->nameItIn($name, $language);
$this->formElement->setName($name, $language);
}
/**
@ -97,13 +101,13 @@ final class ManagingProductOptionsContext implements Context
*/
public function iRenameItToInLanguage(string $language, ?string $name = null): void
{
$this->formElement->nameItIn($name ?? '', $language);
$this->formElement->setName($name ?? '', $language);
}
/**
* @When I do not name it
*/
public function iDoNotNameIt()
public function iDoNotNameIt(): void
{
// Intentionally left blank to fulfill context expectation
}
@ -119,10 +123,19 @@ final class ManagingProductOptionsContext implements Context
/**
* @When I add the :value option value identified by :code
* @When I add the :value option value identified by :code in :localeCode
*/
public function iAddTheOptionValueWithCodeAndValue(string $value, string $code): void
public function iAddTheOptionValueWithCodeAndValue(string $value, string $code, string $localeCode = 'en_US'): void
{
$this->formElement->addOptionValue($code, $value);
$this->formElement->addOptionValue($code, $localeCode, $value);
}
/**
* @When I apply the option value identified by :code in :localeCode to all option values.
*/
public function iApplyToAllTheOptionValueIdentifiedBy(string $code, string $localeCode): void
{
$this->formElement->applyToAllOptionValues($code, $localeCode);
}
/**
@ -130,7 +143,7 @@ final class ManagingProductOptionsContext implements Context
*/
public function iDeleteTheOptionValueWithCodeAndValue(string $value): void
{
$this->formElement->removeOptionValue($value);
// TODO: Implement deleting option value
}
/**
@ -150,21 +163,22 @@ final class ManagingProductOptionsContext implements Context
}
/**
* @Then I should see the product option :productOptionName in the list
* @Then the product option :productOptionName should appear in the registry
* @Then the product option :productOptionName should be in the registry
* @Then I should see the product option :productOption in the list
* @Then the product option :productOption should appear in the registry
* @Then the product option :productOption should be in the registry
*/
public function theProductOptionShouldAppearInTheRegistry(string $productOptionName): void
public function theProductOptionShouldAppearInTheRegistry(ProductOptionInterface $productOption): void
{
$this->sharedStorage->set('product_option', $productOption);
$this->iBrowseProductOptions();
Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $productOptionName]));
Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $productOption->getName()]));
}
/**
* @Then I should be notified that product option with this code already exists
*/
public function iShouldBeNotifiedThatProductOptionWithThisCodeAlreadyExists()
public function iShouldBeNotifiedThatProductOptionWithThisCodeAlreadyExists(): void
{
Assert::same($this->formElement->getValidationMessage('code'), 'The option with given code already exists.');
}
@ -172,7 +186,7 @@ final class ManagingProductOptionsContext implements Context
/**
* @Then there should still be only one product option with :element :value
*/
public function thereShouldStillBeOnlyOneProductOptionWith($element, $value)
public function thereShouldStillBeOnlyOneProductOptionWith(string $element, string $value): void
{
$this->iBrowseProductOptions();
@ -182,15 +196,15 @@ final class ManagingProductOptionsContext implements Context
/**
* @Then I should be notified that :element is required
*/
public function iShouldBeNotifiedThatElementIsRequired($element)
public function iShouldBeNotifiedThatElementIsRequired(string $element): void
{
Assert::same($this->formElement->getValidationMessage($element, ['%localeCode%' => 'en_US']), sprintf('Please enter option %s.', $element));
Assert::same($this->formElement->getValidationMessage($element, ['%locale_code%' => 'en_US']), sprintf('Please enter option %s.', $element));
}
/**
* @Then the product option with :element :value should not be added
*/
public function theProductOptionWithElementValueShouldNotBeAdded($element, $value)
public function theProductOptionWithElementValueShouldNotBeAdded(string $element, string $value): void
{
$this->iBrowseProductOptions();
@ -201,7 +215,7 @@ final class ManagingProductOptionsContext implements Context
* @Then /^(this product option) should still be named "([^"]+)"$/
* @Then /^(this product option) name should be "([^"]+)"$/
*/
public function thisProductOptionNameShouldStillBe(ProductOptionInterface $productOption, $productOptionName)
public function thisProductOptionNameShouldStillBe(ProductOptionInterface $productOption, string $productOptionName): void
{
$this->iBrowseProductOptions();
@ -227,14 +241,6 @@ final class ManagingProductOptionsContext implements Context
// Intentionally left blank to fulfill context expectation
}
/**
* @Then I should be notified that at least two option values are required
*/
public function iShouldBeNotifiedThatAtLeastTwoOptionValuesAreRequired()
{
Assert::true($this->createPage->checkValidationMessageForOptionValues('Please add at least 2 option values.'));
}
/**
* @Then I should see a single product option in the list
* @Then I should see :amount product options in the list
@ -247,28 +253,36 @@ final class ManagingProductOptionsContext implements Context
/**
* @Then /^(this product option) should have the "([^"]*)" option value$/
* @Then /^(product option "[^"]+") should have the "([^"]*)" option value$/
* @Then /^(this product option) should have the "([^"]*)" option value in ("([^"]+)" locale)$/
*/
public function thisProductOptionShouldHaveTheOptionValue(ProductOptionInterface $productOption, $optionValue)
{
public function thisProductOptionShouldHaveTheOptionValue(
ProductOptionInterface $productOption,
string $optionValue,
string $localeCode = 'en_US',
): void {
$this->iWantToModifyAProductOption($productOption);
Assert::true($this->formElement->isThereOptionValue($optionValue));
Assert::true($this->formElement->hasOptionValue($optionValue, $localeCode));
}
/**
* @Then /^(this product option) should not have the "([^"]*)" option value$/
* @Then /^(this product option) should not have the "([^"]*)" option value in ("([^"]+)" locale)$/
*/
public function thisProductOptionShouldNotHaveTheOptionValue(ProductOptionInterface $productOption, string $optionValue): void
{
public function thisProductOptionShouldNotHaveTheOptionValue(
ProductOptionInterface $productOption,
string $optionValue,
string $localeCode = 'en_US',
): void {
$this->iWantToModifyAProductOption($productOption);
Assert::false($this->formElement->isThereOptionValue($optionValue));
Assert::false($this->formElement->hasOptionValue($optionValue, $localeCode));
}
/**
* @Then the first product option in the list should have :field :value
*/
public function theFirstProductOptionInTheListShouldHave($field, $value)
public function theFirstProductOptionInTheListShouldHave(string $field, string $value): void
{
Assert::same($this->indexPage->getColumnFields($field)[0], $value);
}
@ -276,7 +290,7 @@ final class ManagingProductOptionsContext implements Context
/**
* @Then the last product option in the list should have :field :value
*/
public function theLastProductOptionInTheListShouldHave($field, $value)
public function theLastProductOptionInTheListShouldHave(string $field, string $value): void
{
$values = $this->indexPage->getColumnFields($field);

View file

@ -23,7 +23,6 @@ use Webmozart\Assert\Assert;
final readonly class ManagingTaxCategoriesContext implements Context
{
public function __construct(
private IndexPageInterface $indexPage,
private CreatePageInterface $createPage,

View file

@ -50,7 +50,7 @@ final readonly class ManagingTranslatableEntitiesContext implements Context
{
Assert::throws(
fn () => $this->taxonFormElement->describeItAs('Description', $localeCode),
ElementNotFoundException::class
ElementNotFoundException::class,
);
}
}

View file

@ -57,12 +57,9 @@ class FormElement extends Element implements FormElementInterface
*/
protected function getDefinedElements(): array
{
return array_merge(
parent::getDefinedElements(),
[
return array_merge(parent::getDefinedElements(), [
'form' => 'form',
],
);
]);
}
protected function waitForFormUpdate(): void

View file

@ -60,5 +60,4 @@ final class FormElement extends BaseFormElement implements FormElementInterface
'code' => '[data-test-code]',
]);
}
}

View file

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Element\Admin\ExchangeRate;
use Sylius\Behat\Element\Admin\Crud\FormElement as BaseFormElement;
final class FormElement extends BaseFormElement implements FormElementInterface
{
public function isFieldDisabled(string $fieldName): bool
{
return null !== $this->getElement($fieldName)->getAttribute('disabled');
}
public function getRatio(): string
{
return $this->getElement('ratio')->getValue();
}
public function hasFormValidationError(string $expectedMessage): bool
{
return $expectedMessage === $this->getValidationErrors();
}
public function specifyRatio(string $ratio): void
{
$this->getElement('ratio')->setValue($ratio);
}
public function specifySourceCurrency(string $sourceCurrency): void
{
$this->getElement('source_currency')->setValue($sourceCurrency);
}
public function specifyTargetCurrency(string $targetCurrency): void
{
$this->getElement('target_currency')->setValue($targetCurrency);
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'ratio' => '[data-test-ratio]',
'source_currency' => '[data-test-source-currency]',
'target_currency' => '[data-test-target-currency]',
]);
}
}

View file

@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Element\Admin\ExchangeRate;
use Sylius\Behat\Element\Admin\Crud\FormElementInterface as BaseFormElementInterface;
interface FormElementInterface extends BaseFormElementInterface
{
public function isFieldDisabled(string $fieldName): bool;
public function getRatio(): string;
public function hasFormValidationError(string $expectedMessage): bool;
public function specifyRatio(string $ratio): void;
public function specifySourceCurrency(string $sourceCurrency): void;
public function specifyTargetCurrency(string $targetCurrency): void;
}

View file

@ -60,5 +60,4 @@ final class FormElement extends BaseFormElement implements FormElementInterface
'code' => '[data-test-code]',
]);
}
}

View file

@ -14,7 +14,6 @@ declare(strict_types=1);
namespace Sylius\Behat\Element\Admin\Locale;
use Sylius\Behat\Element\Admin\Crud\FormElementInterface as BaseFormElementInterface;
use Sylius\Component\Core\Model\TaxonInterface;
interface FormElementInterface extends BaseFormElementInterface
{

View file

@ -32,7 +32,7 @@ final class AttributesFormElement extends BaseFormElement implements AttributesF
{
$this->changeTab();
$this->selectAttributeToBeAdded($attributeName);
$this->clickButton('Add');
$this->getElement('attribute_add_button')->click();
$this->waitForFormUpdate();
}
@ -40,7 +40,8 @@ final class AttributesFormElement extends BaseFormElement implements AttributesF
public function addSelectedAttributes(): void
{
$this->changeTab();
$this->clickButton('Add');
$this->getElement('attribute_add_button')->click();
$this->waitForFormUpdate();
}
@ -131,9 +132,7 @@ final class AttributesFormElement extends BaseFormElement implements AttributesF
protected function getDefinedElements(): array
{
return array_merge(
parent::getDefinedElements(),
[
return array_merge(parent::getDefinedElements(), [
'attribute_add_button' => '[data-test-attribute-add-button]',
'attribute_autocomplete' => '[data-test-attribute-autocomplete] input[name="product_attributes"]',
'attribute_delete_button' => '[data-test-attribute-delete-button="%attribute_name%"]',
@ -141,8 +140,7 @@ final class AttributesFormElement extends BaseFormElement implements AttributesF
'attribute_tab' => '[data-test-attribute-tab="%name%"]',
'attribute_value' => '[data-test-attribute-value][data-test-locale-code="%locale_code%"][data-test-attribute-name="%attribute_name%"]',
'side_navigation_tab' => '[data-test-side-navigation-tab="%name%"]',
],
);
]);
}
private function selectAttributeToBeAdded(string $attributeName): void
@ -162,11 +160,6 @@ final class AttributesFormElement extends BaseFormElement implements AttributesF
}
}
private function clickButton(string $locator): void
{
$this->getElement('attribute_add_button')->click();
}
private function changeTab(): void
{
if (DriverHelper::isNotJavascript($this->getDriver())) {

View file

@ -45,7 +45,7 @@ final class ChannelPricingsFormElement extends BaseFormElement implements Channe
public function hasNoPriceForChannel(string $channelName): bool
{
return !str_contains($this->getElement('prices')->getHtml(), $channelName);
return !str_contains($this->getElement('channels')->getText(), $channelName);
}
public function getChannelPricingValidationMessage(): string
@ -55,9 +55,7 @@ final class ChannelPricingsFormElement extends BaseFormElement implements Channe
protected function getDefinedElements(): array
{
return array_merge(
parent::getDefinedElements(),
[
return array_merge(parent::getDefinedElements(), [
'channel' => '[data-test-channel-code="%channel_code%"]',
'channel_tab' => '[data-test-channel-tab="%channel_code%"]',
'channels' => '[data-test-channels]',
@ -65,8 +63,7 @@ final class ChannelPricingsFormElement extends BaseFormElement implements Channe
'price' => '[data-test-price-in-channel="%channel_code%"]',
'prices_validation_message' => '[data-test-missing-channel-price]',
'side_navigation_tab' => '[data-test-side-navigation-tab="%name%"]',
],
);
]);
}
private function changeChannelTab(string $channelCode): void

View file

@ -14,92 +14,57 @@ declare(strict_types=1);
namespace Sylius\Behat\Element\Admin\ProductOption;
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Exception\ElementNotFoundException;
use Sylius\Behat\Behaviour\ChecksCodeImmutability;
use Sylius\Behat\Behaviour\NamesIt;
use Sylius\Behat\Behaviour\SpecifiesItsField;
use Sylius\Behat\Element\Admin\Crud\FormElement as BaseFormElement;
use Webmozart\Assert\Assert;
class FormElement extends BaseFormElement implements FormElementInterface
{
use NamesIt;
use SpecifiesItsField;
use ChecksCodeImmutability;
public function addOptionValue(string $code, string $value): void
public function setName(string $name, string $localeCode): void
{
$count = count($this->getProductOptionValues());
$this->getElement('add_value')->click();
$this->getDocument()->waitFor(5, fn () => $count + 1 === count($this->getProductOptionValues()));
$optionValueForm = $this->getLastOptionValueElement();
$optionValueForm->fillField('Code', $code);
$optionValueForm->find('css', '[data-test-option-value-translation]')->setValue($value);
$this->getElement('name', ['%locale_code%' => $localeCode])->setValue($name);
}
public function removeOptionValue(string $optionValue): void
public function addOptionValue(string $code, string $localeCode, string $value): void
{
$count = count($this->getProductOptionValues());
$this->getElement('add_option_value')->press();
$this->waitForFormUpdate();
$optionValues = $this->getProductOptionValues();
foreach ($optionValues as $optionValueElement) {
if ($optionValueElement->has('css', sprintf('input[value="%s"]', $optionValue))) {
$optionValueElement->find('css', '[data-test-delete-value]')->click();
$this->getDocument()->waitFor(5, fn () => $count - 1 === count($this->getProductOptionValues()));
return;
}
}
throw new ElementNotFoundException($this->getSession(), 'option value', 'css', sprintf('input[value="%s"]', $optionValue));
$lastValue = $this->getElement('last_option_value');
$lastValue->find('css', '[data-test-code]')->setValue($code);
$lastValue->find('css', sprintf('[id$="_translations_%s_value"]', $localeCode))->setValue($value);
$this->waitForFormUpdate();
}
public function nameItIn(string $name, string $localeCode): void
public function hasOptionValue(string $optionValue, string $localeCode): bool
{
$this->getElement('name', ['%localeCode%' => $localeCode])->setValue($name);
return $this->hasElement('option_value', ['%option_value%' => $optionValue, '%locale_code%' => $localeCode]);
}
public function isThereOptionValue(string $optionValue): bool
public function applyToAllOptionValues(string $code, string $localeCode): void
{
$optionValues = $this->getElement('values');
$this->getElement('apply_to_all', ['%value_code%' => $code, '%locale_code%' => $localeCode])->click();
$this->waitForFormUpdate();
}
return $optionValues->has('css', sprintf('input[value="%s"]', $optionValue));
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'add_option_value' => '[data-test-add-option-value]',
'apply_to_all' => '[data-test-option-value="%value_code%"] [data-test-option-value-locale="%locale_code%"] [data-test-apply-to-all]',
'code' => '[data-test-code]',
'form' => '[data-live-name-value="sylius_admin:product_option:form"]',
'last_option_value' => '[data-test-option-values] [data-test-option-value]:last-child',
'name' => '#sylius_admin_product_option_translations_%locale_code%_name',
'option_value' => '[data-test-option-values] input[id$="_translations_%locale_code%_value"][value="%option_value%"]',
]);
}
protected function getCodeElement(): NodeElement
{
return $this->getElement('code');
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'add_value' => '[data-test-add-value]',
'code' => '[data-test-code]',
'name' => '#sylius_admin_product_option_translations_%localeCode%_name',
'product_option_value' => '[data-test-product-option-value]',
'values' => '[data-test-values]',
]);
}
private function getLastOptionValueElement(): NodeElement
{
$values = $this->getProductOptionValues();
return end($values);
}
private function getProductOptionValues(): array
{
$items = $this->getElement('values')->findAll('css', '[data-test-product-option-value]');
Assert::isArray($items);
return $items;
}
}

View file

@ -17,15 +17,15 @@ use Sylius\Behat\Element\Admin\Crud\FormElementInterface as BaseFormElementInter
interface FormElementInterface extends BaseFormElementInterface
{
public function specifyCode(string $code): void;
public function isCodeDisabled(): bool;
public function nameItIn(string $name, string $localeCode): void;
public function setName(string $name, string $localeCode): void;
public function isThereOptionValue(string $optionValue): bool;
public function addOptionValue(string $code, string $localeCode, string $value): void;
public function addOptionValue(string $code, string $value): void;
public function hasOptionValue(string $optionValue, string $localeCode): bool;
public function removeOptionValue(string $optionValue): void;
public function specifyCode(string $code): void;
public function applyToAllOptionValues(string $code, string $localeCode): void;
}

View file

@ -1,35 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\Currency;
use Sylius\Behat\Behaviour\ChoosesName;
use Sylius\Behat\Page\Admin\Crud\CreatePage as BaseCreatePage;
class CreatePage extends BaseCreatePage implements CreatePageInterface
{
use ChoosesName;
public function specifyExchangeRate(float $exchangeRate): void
{
$this->getDocument()->fillField('Exchange rate', $exchangeRate);
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'code' => '#sylius_admin_currency_code',
'exchangeRate' => '#sylius_admin_currency_exchangeRate',
]);
}
}

View file

@ -1,23 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\Currency;
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface as BaseCreatePageInterface;
interface CreatePageInterface extends BaseCreatePageInterface
{
public function chooseName(string $name): void;
public function specifyExchangeRate(float $exchangeRate): void;
}

View file

@ -1,53 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\ExchangeRate;
use Sylius\Behat\Page\Admin\Crud\CreatePage as BaseCreatePage;
class CreatePage extends BaseCreatePage implements CreatePageInterface
{
public function specifyRatio(string $ratio): void
{
$this->getDocument()->fillField('Ratio', $ratio);
}
public function chooseSourceCurrency(string $currency): void
{
$this->getDocument()->selectFieldOption('Source currency', $currency);
}
public function chooseTargetCurrency(string $currency): void
{
$this->getDocument()->selectFieldOption('Target currency', $currency);
}
public function hasFormValidationError(string $expectedMessage): bool
{
$formValidationErrors = $this->getDocument()->find('css', 'form > div.ui.red.label.sylius-validation-error');
if (null === $formValidationErrors) {
return false;
}
return $expectedMessage === $formValidationErrors->getText();
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'source currency' => '#sylius_admin_exchange_rate_sourceCurrency',
'target currency' => '#sylius_admin_exchange_rate_targetCurrency',
'ratio' => '#sylius_admin_exchange_rate_ratio',
]);
}
}

View file

@ -1,27 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\ExchangeRate;
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface as BaseCreatePage;
interface CreatePageInterface extends BaseCreatePage
{
public function specifyRatio(string $ratio): void;
public function chooseSourceCurrency(string $currency): void;
public function chooseTargetCurrency(string $currency): void;
public function hasFormValidationError(string $expectedMessage): bool;
}

View file

@ -1,48 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\ExchangeRate;
use Sylius\Behat\Page\Admin\Crud\UpdatePage as BaseUpdatePage;
class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
{
public function getRatio(): string
{
return $this->getElement('ratio')->getValue();
}
public function changeRatio(string $ratio): void
{
$this->getElement('ratio')->setValue($ratio);
}
public function isSourceCurrencyDisabled(): bool
{
return null !== $this->getElement('sourceCurrency')->getAttribute('disabled');
}
public function isTargetCurrencyDisabled(): bool
{
return null !== $this->getElement('targetCurrency')->getAttribute('disabled');
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'ratio' => '#sylius_admin_exchange_rate_ratio',
'sourceCurrency' => '#sylius_admin_exchange_rate_sourceCurrency',
'targetCurrency' => '#sylius_admin_exchange_rate_targetCurrency',
]);
}
}

View file

@ -1,27 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\ExchangeRate;
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface as BaseUpdatePageInterface;
interface UpdatePageInterface extends BaseUpdatePageInterface
{
public function getRatio(): string;
public function changeRatio(string $ratio): void;
public function isSourceCurrencyDisabled(): bool;
public function isTargetCurrencyDisabled(): bool;
}

View file

@ -142,17 +142,15 @@ class UpdateSimpleProductPage extends BaseUpdatePage implements UpdateSimpleProd
protected function getDefinedElements(): array
{
return array_merge(
parent::getDefinedElements(),
[
'code' => '[data-test-code]',
'enabled' => '[data-test-enabled]',
'field_shipping_category' => '[name="sylius_admin_product[variant][shippingCategory]"]',
'field_shipping_required' => '[name="sylius_admin_product[variant][shippingRequired]"]',
'product_translation_accordion' => '[data-test-product-translations-accordion="%localeCode%"]',
'show_product_button' => '[data-test-view-in-store]',
'side_navigation_tab' => '[data-test-side-navigation-tab="%name%"]',
],
);
return array_merge(parent::getDefinedElements(), [
'code' => '[data-test-code]',
'enabled' => '[data-test-enabled]',
'field_shipping_category' => '[name="sylius_admin_product[variant][shippingCategory]"]',
'field_shipping_required' => '[name="sylius_admin_product[variant][shippingRequired]"]',
'product_translation_accordion' => '[data-test-product-translations-accordion="%localeCode%"]',
'show_product_button' => '[data-test-view-in-store]',
'side_navigation_tab' => '[data-test-side-navigation-tab="%name%"]',
'tracked' => '[name="sylius_admin_product[variant][tracked]"]',
]);
}
}

View file

@ -322,7 +322,7 @@ class ShowPage extends SymfonyPage implements ShowPageInterface
} while (!$isOpen && microtime(true) < $end);
if (!$isOpen) {
throw new UnexpectedPageException();
throw new UnexpectedPageException('Is not open: ' . $e->getMessage() . ' ' . json_encode($urlParameters));
}
}

View file

@ -116,6 +116,7 @@
<argument type="service" id="sylius.behat.page.admin.exchange_rate.create" />
<argument type="service" id="sylius.behat.page.admin.exchange_rate.index" />
<argument type="service" id="sylius.behat.page.admin.exchange_rate.update" />
<argument type="service" id="sylius.behat.element.admin.exchange_rate.form" />
</service>
<service id="sylius.behat.context.ui.admin.managing_inventory" class="Sylius\Behat\Context\Ui\Admin\ManagingInventoryContext">
@ -228,6 +229,7 @@
<argument type="service" id="sylius.behat.page.admin.product_option.create" />
<argument type="service" id="sylius.behat.page.admin.product_option.update" />
<argument type="service" id="sylius.behat.element.admin.product_option.form" />
<argument type="service" id="sylius.behat.shared_storage" />
</service>
<service id="sylius.behat.context.ui.admin.managing_product_reviews" class="Sylius\Behat\Context\Ui\Admin\ManagingProductReviewsContext">

View file

@ -137,5 +137,12 @@
>
<argument type="service" id="Sylius\Behat\Service\Helper\AutocompleteHelperInterface" />
</service>
<service
id="sylius.behat.element.admin.exchange_rate.form"
class="Sylius\Behat\Element\Admin\ExchangeRate\FormElement"
parent="sylius.behat.element.admin.crud.form"
>
</service>
</services>
</container>

View file

@ -12,16 +12,20 @@
-->
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sylius.behat.page.admin.exchange_rate.create.class">%sylius.behat.page.admin.crud.create.class%</parameter>
<parameter key="sylius.behat.page.admin.exchange_rate.update.class">%sylius.behat.page.admin.crud.update.class%</parameter>
</parameters>
<services>
<defaults public="true" />
<service id="sylius.behat.page.admin.exchange_rate.create" class="Sylius\Behat\Page\Admin\ExchangeRate\CreatePage" parent="sylius.behat.page.admin.crud.create" public="false">
<service id="sylius.behat.page.admin.exchange_rate.create" class="%sylius.behat.page.admin.exchange_rate.create.class%" parent="sylius.behat.page.admin.crud.create" public="false">
<argument type="string">sylius_admin_exchange_rate_create</argument>
</service>
<service id="sylius.behat.page.admin.exchange_rate.index" class="Sylius\Behat\Page\Admin\ExchangeRate\IndexPage" parent="sylius.behat.page.admin.crud.index" public="false">
<argument type="string">sylius_admin_exchange_rate_index</argument>
</service>
<service id="sylius.behat.page.admin.exchange_rate.update" class="Sylius\Behat\Page\Admin\ExchangeRate\UpdatePage" parent="sylius.behat.page.admin.crud.update" public="false">
<service id="sylius.behat.page.admin.exchange_rate.update" class="%sylius.behat.page.admin.exchange_rate.update.class%" parent="sylius.behat.page.admin.crud.update" public="false">
<argument type="string">sylius_admin_exchange_rate_update</argument>
</service>
</services>

View file

@ -3,7 +3,7 @@
default:
suites:
ui_viewing_products:
ui_accessing_edit_page_from_product_show_page:
contexts:
- sylius.behat.context.hook.doctrine_orm
- sylius.behat.context.hook.session

View file

@ -43,7 +43,7 @@ final class LocaleType extends AbstractType
$locale = $event->getData();
if ($locale instanceof LocaleInterface && null !== $locale->getCode()) {
$options['disabled'] = true;
$options['choices'] = [ Locales::getName($locale->getCode()) => $locale->getCode()];
$options['choices'] = [Locales::getName($locale->getCode()) => $locale->getCode()];
} else {
$options['choices'] = array_flip($this->getAvailableLocales());
}

View file

@ -1,5 +1,14 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Provider;

View file

@ -38,3 +38,7 @@ textarea.form-control {
border-left-width: 2px;
border-left-color: #ff0017;
}
.form-select:disabled {
color: var(--tblr-gray-500);
}

View file

@ -22,21 +22,20 @@ twig_hooks:
'sylius_admin.dashboard.index.content.header':
breadcrumbs:
template: '@SyliusAdmin/dashboard/index/content/header/breadcrumbs.html.twig'
enabled: false
title_block:
template: '@SyliusAdmin/dashboard/index/content/header/title_block/title.html.twig'
channel_selector:
component: 'sylius_admin:dashboard:channel_selector'
props:
channelCode: '@=_context.channel_code'
template: '@SyliusAdmin/shared/crud/common/content/header/title_block.html.twig'
'sylius_admin.dashboard.index.content.header.title_block':
title:
template: '@SyliusAdmin/shared/crud/common/content/header/title_block/title.html.twig'
configuration:
title: sylius.ui.dashboard
sylius_test_html_attribute: 'dashboard-header'
actions:
enabled: false
component: 'sylius_admin:dashboard:channel_selector'
props:
channelCode: '@=_context.channel_code'
'sylius_admin.dashboard.index.content.statistics':
statistics_grid:

View file

@ -0,0 +1,23 @@
twig_hooks:
hooks:
'sylius_admin.exchange_rate.create.content':
form:
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false
'sylius_admin.exchange_rate.create.content.form':
sections:
template: '@SyliusAdmin/exchange_rate/form/sections.html.twig'
'sylius_admin.exchange_rate.create.content.form.sections':
general:
template: '@SyliusAdmin/exchange_rate/form/sections/general.html.twig'
'sylius_admin.exchange_rate.create.content.form.sections.general':
ratio:
template: '@SyliusAdmin/exchange_rate/form/sections/general/ratio.html.twig'
source_currency:
template: '@SyliusAdmin/exchange_rate/form/sections/general/source_currency.html.twig'
target_currency:
template: '@SyliusAdmin/exchange_rate/form/sections/general/target_currency.html.twig'

View file

@ -0,0 +1,23 @@
twig_hooks:
hooks:
'sylius_admin.exchange_rate.update.content':
form:
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false
'sylius_admin.exchange_rate.update.content.form':
sections:
template: '@SyliusAdmin/exchange_rate/form/sections.html.twig'
'sylius_admin.exchange_rate.update.content.form.sections':
general:
template: '@SyliusAdmin/exchange_rate/form/sections/general.html.twig'
'sylius_admin.exchange_rate.update.content.form.sections.general':
ratio:
template: '@SyliusAdmin/exchange_rate/form/sections/general/ratio.html.twig'
source_currency:
template: '@SyliusAdmin/exchange_rate/form/sections/general/source_currency.html.twig'
target_currency:
template: '@SyliusAdmin/exchange_rate/form/sections/general/target_currency.html.twig'

View file

@ -12,11 +12,22 @@ twig_hooks:
isSimple: '@=_context.resource.isSimple()'
configuration:
method: 'PUT'
'sylius_admin.product.update.content.header.title_block.actions':
view_in_store:
template: '@SyliusAdmin/product/common/header/title_block/actions/view_in_store.html.twig'
priority: 32
priority: 0
manage_variants:
template: '@SyliusAdmin/product/common/header/title_block/actions/manage_variants.html.twig'
priority: 300
'sylius_admin.product.update.content.header.title_block.actions.manage_variants':
list:
template: '@SyliusAdmin/product/common/header/title_block/actions/manage_variants/list.html.twig'
create:
template: '@SyliusAdmin/product/common/header/title_block/actions/manage_variants/create.html.twig'
generate:
template: '@SyliusAdmin/product/common/header/title_block/actions/manage_variants/generate.html.twig'
'sylius_admin.product.update.content.form':
side_navigation:

View file

@ -14,9 +14,11 @@ sylius_grid:
sortable: ~
sourceCurrency:
type: string
path: sourceCurrency.name
label: sylius.ui.source_currency
targetCurrency:
type: string
path: targetCurrency.name
label: sylius.ui.target_currency
ratio:
type: string

View file

@ -9,11 +9,4 @@ sylius_admin_exchange_rate:
form:
type: Sylius\Bundle\AdminBundle\Form\Type\ExchangeRateType
permission: true
vars:
all:
subheader: sylius.ui.manage_exchange_rates
templates:
form: "@SyliusAdmin/ExchangeRate/_form.html.twig"
index:
icon: sliders
type: sylius.resource

View file

@ -18,6 +18,8 @@ use Sylius\TwigHooks\LiveComponent\HookableLiveComponentTrait;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\LiveComponent\LiveCollectionTrait;
@ -43,4 +45,14 @@ class FormComponent
{
return $this->formFactory->create($this->formClass, $this->resource);
}
#[LiveAction]
public function applyToAll(#[LiveArg] string $valueKey, #[LiveArg] string $translationKey): void
{
$value = $this->formValues['values'][$valueKey]['translations'][$translationKey]['value'];
foreach ($this->formValues['values'][$valueKey]['translations'] as &$translation) {
$translation['value'] = $value;
}
}
}

View file

@ -29,6 +29,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
final class LoggedInAdminUserProviderSpec extends ObjectBehavior
{
private const SECURITY_SESSION_KEY = '_security_admin';
private const SERIALIZED_TOKEN = 'O:74:"Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken":3:{i:0;N;i:1;s:5:"admin";i:2;a:5:{i:0;O:37:"Sylius\Component\Core\Model\AdminUser":8:{i:0;s:38:"sylius{x33enl1y0askgkgw8k0skocc4ko0kg}";i:1;s:30:"x33enl1y0askgkgw8k0skocc4ko0kg";i:2;s:6:"sylius";i:3;s:6:"sylius";i:4;b:0;i:5;b:1;i:6;i:404;i:7;s:9:"plaintext";}i:1;b:1;i:2;N;i:3;a:0:{}i:4;a:1:{i:0;s:26:"ROLE_ADMINISTRATION_ACCESS";}}}';
function let(

View file

@ -1,13 +1,12 @@
{# Rendered with \Sylius\Bundle\AdminBundle\Twig\Component\Dashboard\ChannelSelectorComponent #}
{% if channels|length > 1 %}
<div class="d-flex justify-content-end" {{ attributes }}>
<div class="col-12 col-md-auto ms-auto d-print-none" {{ attributes }}>
<div class="dropdown">
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-bs-toggle="dropdown" aria-expanded="false" {{ sylius_test_html_attribute('choose-channel-button') }}>
{{ channel_name }}
</a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuLink" {{ sylius_test_html_attribute('choose-channel-list') }}>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownMenuLink" {{ sylius_test_html_attribute('choose-channel-list') }}>
{% for channel in channels %}
<li>
<a

View file

@ -1,5 +0,0 @@
{% import '@SyliusAdmin/shared/helper/breadcrumbs.html.twig' as _breadcrumbs %}
{{ _breadcrumbs.default([
{ 'name': 'Dashboard', 'active': true },
]) }}

View file

@ -1,7 +0,0 @@
{% import '@SyliusAdmin/shared/helper/header.html.twig' as _header %}
<div class="col-12 col-md-6 mb-3 mt-3">
<div class="d-md-flex gap-2 align-items-center" {{ sylius_test_html_attribute('dashboard-header') }}>
{{ _header.h1('sylius.ui.dashboard'|trans) }}
</div>
</div>

View file

@ -10,29 +10,11 @@
</div>
</div>
<div class="card-body">
<div class="d-flex justify-content-end">
<div class="d-flex justify-content-end gap-5 mb-3">
<div class="btn-group" role="group">
<button
type="button"
class="btn navigation navigation-prev"
data-action="live#action"
data-live-action-param="prevent|getPreviousPeriod"
{{ sylius_test_html_attribute('previous-period') }}
>
{{ icon({ icon: 'arrow-left', class: 'icon dropdown-item-icon icon-tabler' }) }}
</button>
<button
type="button"
class="btn navigation navigation-prev"
data-action="live#action"
data-live-action-param="prevent|getNextPeriod"
{{ sylius_test_html_attribute('next-period') }}
>
{{ icon({ icon: 'arrow-right', class: 'icon dropdown-item-icon icon-tabler' }) }}
</button>
<button
type="button"
class="btn {% if period == '2 weeks' %}btn-secondary{% else %}btn-outline-secondary{% endif %}"
class="btn {% if period == '2 weeks' %}btn-light border{% endif %}"
data-action="live#action"
data-live-action-param="prevent|changeRange"
data-live-period-param="2 weeks"
@ -42,7 +24,7 @@
</button>
<button
type="button"
class="btn {% if period == 'month' %}btn-secondary{% else %}btn-outline-secondary{% endif %}"
class="btn {% if period == 'month' %}btn-light border{% endif %}"
data-action="live#action"
data-live-action-param="prevent|changeRange"
data-live-period-param="month"
@ -52,7 +34,7 @@
</button>
<button
type="button"
class="btn {% if period == 'year' %}btn-secondary{% else %}btn-outline-secondary{% endif %}"
class="btn {% if period == 'year' %}btn-light border{% endif %}"
data-action="live#action"
data-live-action-param="prevent|changeRange"
data-live-period-param="year"
@ -62,6 +44,26 @@
{{ 'sylius.ui.statistics.year'|trans }}
</button>
</div>
<div class="btn-group" role="group">
<button
type="button"
class="btn btn-icon navigation navigation-prev"
data-action="live#action"
data-live-action-param="prevent|getPreviousPeriod"
{{ sylius_test_html_attribute('previous-period') }}
>
{{ icon({ icon: 'arrow-left', class: 'icon dropdown-item-icon icon-tabler' }) }}
</button>
<button
type="button"
class="btn btn-icon navigation navigation-prev"
data-action="live#action"
data-live-action-param="prevent|getNextPeriod"
{{ sylius_test_html_attribute('next-period') }}
>
{{ icon({ icon: 'arrow-right', class: 'icon dropdown-item-icon icon-tabler' }) }}
</button>
</div>
</div>
<div id="statistics-chart" class="chart-lg" data-intervals="{{ statistics_data.sales_summary.intervals|json_encode }}" data-sales="{{ statistics_data.sales_summary.sales|json_encode }}" data-currency="{{ statistics_data.currency_symbol }}"></div>
</div>

View file

@ -0,0 +1,3 @@
<div class="row">
{% hook 'sections' %}
</div>

View file

@ -0,0 +1,14 @@
<div class="col-12 p-2">
<div class="card">
<div class="card-header">
<div class="card-title">
{{ 'sylius.ui.general'|trans }}
</div>
</div>
<div class="card-body">
<div class="row align-items-center">
{% hook 'general' %}
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,3 @@
<div class="mb-3">
{{ form_row(hookable_metadata.context.form.ratio, sylius_test_form_attribute('ratio')) }}
</div>

View file

@ -0,0 +1,3 @@
<div class="mb-3">
{{ form_row(hookable_metadata.context.form.sourceCurrency, sylius_test_form_attribute('source-currency')) }}
</div>

View file

@ -0,0 +1,3 @@
<div class="mb-3">
{{ form_row(hookable_metadata.context.form.targetCurrency, sylius_test_form_attribute('target-currency')) }}
</div>

View file

@ -0,0 +1,8 @@
<div class="dropdown">
<button class="btn dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ 'sylius.ui.manage_variants'|trans }}
</button>
<ul class="dropdown-menu" {{ sylius_test_html_attribute('manage-variants') }}>
{% hook 'manage_variants' %}
</ul>
</div>

View file

@ -0,0 +1,10 @@
{% from '@SyliusAdmin/shared/helper/icon.html.twig' import icon %}
{% set product = hookable_metadata.context.resource %}
<li>
<a class="dropdown-item" href="{{ path('sylius_admin_product_variant_create', {'productId': product.id}) }}">
{{ icon({ icon: 'plus', class: 'icon dropdown-item-icon icon-tabler' }) }}
{{ 'sylius.ui.create'|trans }}
</a>
</li>

View file

@ -0,0 +1,10 @@
{% from '@SyliusAdmin/shared/helper/icon.html.twig' import icon %}
{% set product = hookable_metadata.context.resource %}
<li>
<a class="dropdown-item" href="{{ path('sylius_admin_product_variant_generate', {'productId': product.id}) }}">
{{ icon({ icon: 'arrows-split', class: 'icon dropdown-item-icon icon-tabler' }) }}
{{ 'sylius.ui.generate'|trans }}
</a>
</li>

View file

@ -0,0 +1,10 @@
{% from '@SyliusAdmin/shared/helper/icon.html.twig' import icon %}
{% set product = hookable_metadata.context.resource %}
<li>
<a class="dropdown-item" href="{{ path('sylius_admin_product_variant_index', {'productId': product.id}) }}">
{{ icon({ icon: 'list-details', class: 'icon dropdown-item-icon icon-tabler' }) }}
{{ 'sylius.ui.list_variants'|trans }}
</a>
</li>

View file

@ -41,7 +41,7 @@
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product_translation.slug, '_locale': product_translation.locale}), channel) %}
{% endif %}
<a class="btn" href="{{ (url is not defined) ? '#' : url|raw }}" target="_blank" {{ sylius_test_html_attribute('view-in-store') }}>
<a class="btn {% if url is not defined %}disabled{% endif %}" href="{{ (url is not defined) ? '#' : url|raw }}" target="_blank" {{ sylius_test_html_attribute('view-in-store') }}>
{{ _icon({ icon: 'arrow-up-right', class: 'icon icon-tabler' }) }}
{{ 'sylius.ui.view_in_store'|trans }}
</a>

View file

@ -78,9 +78,12 @@
{{ form_label(product_attribute_value.value, null, {label_attr: {class: required_class}}) }}
{% if locale_code is not null %}
<button
type="button"
class="btn btn-secondary btn-sm"
data-action="live#action"
data-live-action-param="prevent|applyToAll(attributeCode={{ product_attribute_code }},localeCode={{ product_attribute_value.vars.data.localeCode }})"
data-live-action-param="applyToAll"
data-live-attribute-code-param="{{ product_attribute_code }}"
data-live-locale-code-param="{{ product_attribute_value.vars.data.localeCode }}"
>
{{ 'sylius.ui.apply_to_all'|trans }}
</button>

View file

@ -3,14 +3,13 @@
<div class="card-title">{{ 'sylius.ui.values'|trans }}</div>
</div>
<div class="card-body" >
<div class="row" {{ sylius_test_html_attribute('values') }}>
<div class="row" {{ sylius_test_html_attribute('option-values') }}>
{{ form_errors(hookable_metadata.context.form.values) }}
{% for value_form in hookable_metadata.context.form.values %}
{% hook 'values' with { value_form } %}
{% for value_form_key, value_form in hookable_metadata.context.form.values %}
{% hook 'values' with { value_form, value_form_key } %}
{% endfor %}
{{ form_row(hookable_metadata.context.form.values.vars.button_add, sylius_test_form_attribute('add-value')) }}
</div>
{{ form_row(hookable_metadata.context.form.values.vars.button_add, sylius_test_form_attribute('add-option-value')) }}
</div>
</div>

View file

@ -1,9 +1,7 @@
<div class="card mb-3">
<div class="card-body" {{ sylius_test_html_attribute('product-option-value') }}>
<div class="row align-items-end" >
<div class="col">
{% hook 'value' with { form: hookable_metadata.context.value_form } %}
</div>
<div class="col-12 col-md-6" {{ sylius_test_html_attribute('option-value', hookable_metadata.context.value_form.code.vars.value) }}>
<div class="card mb-3">
<div class="card-body">
{% hook 'value' with { form: hookable_metadata.context.value_form } %}
</div>
</div>
</div>

View file

@ -1,25 +1,34 @@
{% for translation_form in hookable_metadata.context.value_form.translations %}
<div class="row">
<div class="col">
{% if translation_form.vars.name is not null %}
{% from '@SyliusAdmin/shared/helper/icon.html.twig' import icon %}
{% for translation_form_key, translation_form in hookable_metadata.context.value_form.translations %}
<div class="row" {{ sylius_test_html_attribute('option-value-locale', translation_form_key) }}>
<div class="col-3">
{% if translation_form.vars.name is not null %}
<div class="d-flex">
<span class="flag flag-sm flag-country-{{ translation_form.vars.name|lower|split('_')|last }} me-2 mt-3"></span>
<div class="me-3 mt-1">{{ form_label(translation_form, translation_form.vars.name|sylius_locale_name) }}</div>
</div>
{% else %}
<span class="flag flag-sm flag-country-eu me-3"></span>
{% endif %}
{% else %}
<span class="flag flag-sm flag-country-eu me-3"></span>
{% endif %}
</div>
<div class="col-9">
<div class="d-flex">
<div class="p-2 w-100">
{{ form_widget(
translation_form.value,
sylius_test_form_attribute('option-value-translation')
) }}
{{ form_errors(translation_form.value) }}
</div>
<div class="input-group">
{{ form_widget(translation_form.value) }}
<button
type="button"
class="btn btn-icon"
data-bs-toggle="tooltip"
data-bs-title="{{ 'sylius.ui.apply_to_all'|trans }}"
data-action="live#action:prevent"
data-live-action-param="applyToAll"
data-live-value-key-param="{{ hookable_metadata.context.value_form_key }}"
data-live-translation-key-param="{{ translation_form_key }}"
{{ sylius_test_html_attribute('apply-to-all') }}
>
{{ icon({ icon: 'duplicate', class: 'icon icon-tabler' }) }}
</button>
{{ form_errors(translation_form.value) }}
</div>
</div>
</div>

View file

@ -7,8 +7,9 @@
{% set header = hookable_metadata.configuration.title %}
{% endif %}
{% set test_attribute = hookable_metadata.configuration.sylius_test_html_attribute is defined ? sylius_test_html_attribute(hookable_metadata.configuration.sylius_test_html_attribute) : null %}
<div class="col-12 col-md">
<div class="col-12 col-md" {{ test_attribute }}>
<div class="d-md-flex gap-2 align-items-center">
{{ _header.h1(header|trans) }}
</div>

View file

@ -371,5 +371,14 @@
<path d="M17 17m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M5 17h-2v-11a1 1 0 0 1 1 -1h9v12m-4 0h6m4 0h2v-6h-8m0 -5h5l3 5" />
{% endif %}
{% if icon == 'duplicate' %}
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path stroke="none" d="M0 0h24v24H0z"></path>
<path d="M7 9.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667z"></path>
<path d="M4.012 16.737a2 2 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1"></path>
<path d="M11 14h6"></path>
<path d="M14 11v6"></path>
{% endif %}
</svg>
{% endmacro %}

View file

@ -1,7 +1,7 @@
{% from '@SyliusAdmin/shared/helper/icon.html.twig' import icon %}
<div class="dropdown">
<button class="btn btn-icon btn-sm" type="button" data-bs-toggle="dropdown" {{ sylius_test_html_attribute('actions') }}>
<button class="btn btn-icon btn-sm" type="button" data-bs-toggle="dropdown" data-bs-popper-config='{"strategy":"fixed"}' {{ sylius_test_html_attribute('actions') }}>
{{ icon({ icon: 'dots', class: 'icon' }) }}
</button>
<ul class="dropdown-menu">

View file

@ -29,6 +29,11 @@
<option name="message">sylius.exchange_rate.ratio.greater_than</option>
<option name="groups">sylius</option>
</constraint>
<constraint name="LessThan">
<option name="value">100000</option>
<option name="groups">sylius</option>
<option name="message">sylius.exchange_rate.ratio.less_than</option>
</constraint>
</property>
<property name="sourceCurrency">
<constraint name="NotBlank">

View file

@ -8,7 +8,8 @@ sylius:
not_blank: Please enter exchange rate.
exchange_rate:
ratio:
greater_than: The ratio must be greater than 0.
greater_than: The ratio must be greater than {{ compared_value }}.
less_than: The ratio must be less than {{ compared_value }}.
invalid: The ratio must be a number.
not_blank: Please enter exchange rate ratio.
source_currency:

View file

@ -22,7 +22,7 @@ final readonly class ErrorTemplateFinder implements ErrorTemplateFinderInterface
{
public function __construct(
private SectionProviderInterface $sectionProvider,
private Environment $twig
private Environment $twig,
) {
}