Merge branch '2.0' into bootstrap-shop

* 2.0: (34 commits)
  [Admin] Change the name of the service to provide the logged in admin user
  [Admin] Extract logged in admin user providing
  [Admin] Fix 404 page when there is no route for url
  [Admin] Remove icon from existing avatar
  Fix missing template
  Fixes after CR
  [Admin] Add translation
  [Admin] Add twig hook, refactor behats
  [Admin] Add autocomplete and restrict list
  [Admin] Remove unused edit currency scenario
  [Admin] Enable currency behats
  [Docs] Update taxon-menu page to reflect current fixtures configuration
  [Admin] Fix path to form template in Twig Hooks for Tax Category
  [Admin] Remove unneeded deprecation
  [Admin][TaxCategory] Add filtering tests
  [Admin][TaxCategory] Enable behat tests
  [Admin] Managing tax category
  [Admin] Use the same generic form template for create and update actions
  [Admin] Add customer group live component that uses generic form template
  [Behat] Improve CustomerGroupsContext codestyle
  ...
This commit is contained in:
Grzegorz Sadowski 2024-07-09 07:28:57 +02:00
commit ec1b8bdd52
No known key found for this signature in database
GPG key ID: EF87FF4E6E3BD364
123 changed files with 1545 additions and 711 deletions

View file

@ -24,7 +24,7 @@ To render a simple menu of categories in any twig template use:
.. code-block:: twig
{{ render(url('sylius_shop_partial_taxon_index_by_code', {'code': 'category', 'template': '@SyliusShop/Taxon/_horizontalMenu.html.twig'})) }}
{{ render(url('sylius_shop_partial_taxon_index_by_code', {'code': 'MENU_CATEGORY', 'template': '@SyliusShop/Taxon/_horizontalMenu.html.twig'})) }}
You can of course customize the template or enclose the menu into html to make it look better.

View file

@ -7,7 +7,7 @@ Feature: Adding a new currency
Background:
Given I am logged in as an administrator
@todo @ui @api
@api @ui @mink:chromedriver
Scenario: Adding a new currency
When I want to add a new currency
And I choose "Euro"

View file

@ -8,8 +8,8 @@ Feature: Browsing currencies
Given the store has currency "Euro", "British Pound"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Browsing currencies in store
When I want to browse currencies of the store
Then I should see 2 currencies in the list
And I should see the currency "British Pound" in the list
Then I should see 2 currencies on the list
And I should see the currency "British Pound" on the list

View file

@ -8,10 +8,15 @@ Feature: Currency unique code validation
Given the store has currency "Euro"
And I am logged in as an administrator
@todo @ui @api
@api @no-ui
Scenario: Trying to add currency with taken code
When I want to add a new currency
And I choose "Euro"
And I try to add it
Then I should be notified that currency code must be unique
And there should still be only one currency with code "EUR"
@no-api @ui @mink:chromedriver
Scenario: Trying to add new currency with used code
When I want to add a new currency
Then I should not be able to choose "Euro"

View file

@ -1,14 +0,0 @@
@managing_currencies
Feature: Editing a currency
In order to change currency configuration
As an Administrator
I want to be able to edit a currency
Background:
Given I am logged in as an administrator
@todo @ui @no-api
Scenario: Seeing disabled code field while editing currency
Given the store has currency "Euro"
When I want to edit this currency
Then the code field should be disabled

View file

@ -8,18 +8,18 @@ Feature: Adding a new payment method
Given the store operates on a single channel in "United States"
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Adding a new payment method
When I want to create a new Offline payment method
When I want to create a new offline payment method
And I name it "Offline" in "English (United States)"
And I specify its code as "OFF"
And I add it
Then I should be notified that it has been successfully created
And the payment method "Offline" should appear in the registry
@ui @api
@api @ui
Scenario: Adding a new payment method with description
When I want to create a new Offline payment method
When I want to create a new offline payment method
And I name it "Offline" in "English (United States)"
And I specify its code as "OFF"
And I describe it as "Payment method Offline" in "English (United States)"
@ -27,9 +27,9 @@ Feature: Adding a new payment method
Then I should be notified that it has been successfully created
And the payment method "Offline" should appear in the registry
@ui @api
@api @ui
Scenario: Adding a new payment method with instructions
When I want to create a new Offline payment method
When I want to create a new offline payment method
And I name it "Offline" in "English (United States)"
And I specify its code as "OFF"
And I set its instruction as "Bank account: 0000 1111 2222 3333" in "English (United States)"
@ -38,9 +38,9 @@ Feature: Adding a new payment method
And the payment method "Offline" should appear in the registry
And the payment method "Offline" should have instructions "Bank account: 0000 1111 2222 3333" in "English (United States)"
@ui @api
@api @ui
Scenario: Adding a new payment method for channel
When I want to create a new Offline payment method
When I want to create a new offline payment method
And I name it "Offline" in "English (United States)"
And I specify its code as "OFF"
And make it available in channel "United States"
@ -49,7 +49,7 @@ Feature: Adding a new payment method
And the payment method "Offline" should appear in the registry
And the payment method "Offline" should be available in channel "United States"
@ui @api
@api @ui
Scenario: Adding a new paypal payment method
When I want to create a new payment method with "Paypal Express Checkout" gateway factory
And I name it "Paypal Express Checkout" in "English (United States)"
@ -59,7 +59,7 @@ Feature: Adding a new payment method
Then I should be notified that it has been successfully created
And the payment method "Paypal Express Checkout" should appear in the registry
@ui @api
@api @ui
Scenario: Adding a new stripe payment method
When I want to create a new payment method with "Stripe Checkout" gateway factory
And I name it "Stripe Checkout" in "English (United States)"

View file

@ -11,7 +11,7 @@ Feature: Being redirected to previous filtered page
And this payment method has been disabled
And I am logged in as an administrator
@todo @ui @no-api
@no-api @ui
Scenario: Being redirected to previous filtered page after cancelling creating a new payment method
When I browse payment methods
And I choose enabled filter

View file

@ -7,7 +7,7 @@ Feature: Browsing payment methods
Background:
Given I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Browsing defined payment methods
Given the store has a payment method "Offline" with a code "OFF"
And the store has a payment method "PayPal Express Checkout" with a code "PAYPAL" and "Paypal Express Checkout" gateway

View file

@ -10,7 +10,7 @@ Feature: Deleting multiple payment methods
And the store has also a payment method "PayPal Express Checkout" with a code "paypal" and "Paypal Express Checkout" gateway
And I am logged in as an administrator
@todo @ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver
Scenario: Deleting multiple payment methods at once
When I browse payment methods
And I check the "Offline" payment method

View file

@ -8,7 +8,7 @@ Feature: Deleting payment methods
Given the store has a payment method "Offline" with a code "Offline"
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Deleted payment method should disappear from the registry
When I delete the "Offline" payment method
Then I should be notified that it has been successfully deleted

View file

@ -9,7 +9,7 @@ Feature: Editing payment methods
And the store has a payment method "Offline" with a code "Offline"
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Renaming the payment method
When I want to modify the "Offline" payment method
And I rename it to "Cash on delivery" in "English (United States)"
@ -17,7 +17,7 @@ Feature: Editing payment methods
Then I should be notified that it has been successfully edited
And this payment method "Name" should be "Cash on delivery"
@ui @api
@api @ui
Scenario: Disabling payment method
When I want to modify the "Offline" payment method
And I disable it
@ -25,7 +25,7 @@ Feature: Editing payment methods
Then I should be notified that it has been successfully edited
And this payment method should be disabled
@ui @api
@api @ui
Scenario: Enabling payment method
Given the payment method "Offline" is disabled
When I want to modify the "Offline" payment method
@ -34,12 +34,12 @@ Feature: Editing payment methods
Then I should be notified that it has been successfully edited
And this payment method should be enabled
@ui @api
@api @ui
Scenario: Being unable to edit code of an existing payment method
When I want to modify the "Offline" payment method
Then I should not be able to edit its code
@ui @api
@api @ui
Scenario: Being unable to edit gateway factory field of existing payment method
When I want to modify the "Offline" payment method
Then the factory name field should be disabled

View file

@ -9,7 +9,7 @@ Feature: Editing payment method configured with a PayPal Express Checkout gatewa
And the store has a payment method "PayPal Express Checkout" with a code "paypal" and "Paypal Express Checkout" gateway
And I am logged in as an administrator
@ui @no-api
@no-api @ui
Scenario: Changing gateway username
When I want to modify the "PayPal Express Checkout" payment method
And I update its "Username" with "new_username"
@ -17,7 +17,7 @@ Feature: Editing payment method configured with a PayPal Express Checkout gatewa
Then I should be notified that it has been successfully edited
And its gateway configuration "Username" should be "new_username"
@ui @no-api
@no-api @ui
Scenario: Changing gateway password
When I want to modify the "PayPal Express Checkout" payment method
And I update its "Password" with "new_password"
@ -25,7 +25,7 @@ Feature: Editing payment method configured with a PayPal Express Checkout gatewa
Then I should be notified that it has been successfully edited
And its gateway configuration "Password" should be "new_password"
@ui @no-api
@no-api @ui
Scenario: Changing gateway signature
When I want to modify the "PayPal Express Checkout" payment method
And I update its "Signature" with "new_signature"
@ -33,7 +33,7 @@ Feature: Editing payment method configured with a PayPal Express Checkout gatewa
Then I should be notified that it has been successfully edited
And its gateway configuration "Signature" should be "new_signature"
@ui @no-api
@no-api @ui
Scenario: Changing gateway sandbox mode
When I want to modify the "PayPal Express Checkout" payment method
And I enable sandbox mode

View file

@ -9,7 +9,7 @@ Feature: Editing payment method configured with a Stripe Checkout gateway
And the store has a payment method "Stripe Checkout" with a code "stripe" and "Stripe Checkout" gateway
And I am logged in as an administrator
@ui @no-api
@no-api @ui
Scenario: Changing Stripe Checkout gateway publishable key
When I want to modify the "Stripe Checkout" payment method
And I update its "Publishable key" with "some_publishable_key"
@ -17,7 +17,7 @@ Feature: Editing payment method configured with a Stripe Checkout gateway
Then I should be notified that it has been successfully edited
And this payment method "Publishable key" should be "some_publishable_key"
@ui @no-api
@no-api @ui
Scenario: Changing Stripe Checkout gateway secret key
When I want to modify the "Stripe Checkout" payment method
And I update its "Secret key" with "some_secret_key"

View file

@ -0,0 +1,33 @@
@managing_payment_methods
Feature: Filtering payment methods
In order to quickly find the payment method I need
As an Administrator
I want to filter available payment methods
Background:
Given the store operates on a single channel in "United States"
And the store has a payment method "PayPal Express Checkout" with a code "paypal_xyz" and "Paypal Express Checkout" gateway
And the store has a payment method "Offline" with a code "offline_abc"
And this payment method is disabled
And the store has a payment method "Cash on Delivery" with a code "cash_on_delivery_xyz"
And I am logged in as an administrator
And I am browsing payment methods
@todo-api @ui
Scenario: Filtering payment methods by name
When I search by "PayPal" name
Then I should see a single payment method in the list
And I should see the payment method "PayPal Express Checkout"
@todo-api @ui
Scenario: Filtering payment methods by code
When I search by "xyz" code
Then I should see 2 payment methods in the list
And I should not see the payment method "Offline"
@todo-api @ui
Scenario: Filtering enabled payment methods
When I choose enabled filter
And I filter
Then I should see 2 payment methods in the list
And I should not see the payment method "Offline"

View file

@ -9,9 +9,9 @@ Feature: Payment method unique code validation
And the store has a payment method "Offline" with a code "Offline"
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Trying to add payment method with taken code
When I want to create a new Offline payment method
When I want to create a new offline payment method
And I name it "Paypal Express Checkout" in "English (United States)"
And I specify its code as "Offline"
And I try to add it

View file

@ -9,7 +9,7 @@ Feature: Payment method validation
And the store has a payment method "Offline" with a code "Offline"
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Trying to add a new payment method without specifying its code
When I want to create a new payment method with "Paypal Express Checkout" gateway factory
And I name it "Paypal Express Checkout" in "English (United States)"
@ -18,14 +18,14 @@ Feature: Payment method validation
Then I should be notified that code is required
And the payment method with name "Paypal Express Checkout" should not be added
@no-ui @api
@api @no-ui
Scenario: Trying to add a new payment method translation in unexisting language
When I want to modify the "Offline" payment method
And I name it "Offline" 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 payment method with a too long code
When I want to create a new payment method with "Paypal Express Checkout" gateway factory
And I name it "Paypal Express Checkout" in "English (United States)"
@ -33,7 +33,7 @@ Feature: Payment method validation
And I add it
Then I should be notified that code is too long
@ui @api
@api @ui
Scenario: Trying to add a new payment method without specifying its name
When I want to create a new payment method with "Paypal Express Checkout" gateway factory
And I specify its code as "PEC"
@ -42,7 +42,7 @@ Feature: Payment method validation
Then I should be notified that I have to specify payment method name
And the payment method with code "PEC" should not be added
@ui @api
@api @ui
Scenario: Trying to add a new paypal payment method without specifying password
When I want to create a new payment method with "Paypal Express Checkout" gateway factory
And I name it "Paypal Express Checkout" in "English (United States)"
@ -53,7 +53,7 @@ Feature: Payment method validation
Then I should be notified that I have to specify paypal password
And the payment method with code "PEC" should not be added
@no-ui @api
@api @no-ui
Scenario: Trying to add a new paypal payment method without specifying sandbox
When I want to create a new payment method with "Paypal Express Checkout" gateway factory
And I name it "Paypal Express Checkout" in "English (United States)"
@ -63,7 +63,7 @@ Feature: Payment method validation
Then I should be notified that I have to specify paypal sandbox status
And the payment method with code "PEC" should not be added
@no-ui @api
@api @no-ui
Scenario: Trying to add a new paypal payment method, but with sandbox that has wrong type
When I want to create a new payment method with "Paypal Express Checkout" gateway factory
And I name it "Paypal Express Checkout" in "English (United States)"
@ -73,7 +73,7 @@ Feature: Payment method validation
Then I should be notified that I have to specify paypal sandbox status that is boolean
And the payment method with code "PEC" should not be added
@ui @api
@api @ui
Scenario: Trying to add a new stripe payment method with only publishable key specified
When I want to create a new payment method with "Stripe Checkout" gateway factory
And I name it "Stripe Checkout" in "English (United States)"
@ -83,7 +83,7 @@ Feature: Payment method validation
Then I should be notified that I have to specify stripe "Secret key"
And the payment method with code "PEC" should not be added
@ui @api
@api @ui
Scenario: Trying to add a new stripe payment method with only secret key specified
When I want to create a new payment method with "Stripe Checkout" gateway factory
And I name it "Stripe Checkout" in "English (United States)"
@ -93,7 +93,7 @@ Feature: Payment method validation
Then I should be notified that I have to specify stripe "Publishable key"
And the payment method with code "PEC" should not be added
@no-ui @api
@api @no-ui
Scenario: Trying to add a new payment method without gateway configuration
When I want to create a new payment method without gateway configuration
And I name it "Payment method without gateway configuration" in "English (United States)"
@ -101,7 +101,7 @@ Feature: Payment method validation
And I try to add it
Then I should be notified that I have to specify gateway configuration
@no-ui @api
@api @no-ui
Scenario: Trying to add a new payment method without gateway name
When I want to create a new payment method without gateway name
And I name it "Payment method without gateway name" in "English (United States)"
@ -109,7 +109,7 @@ Feature: Payment method validation
And I try to add it
Then I should be notified that I have to specify gateway name
@no-ui @api
@api @no-ui
Scenario: Trying to add a new payment method without factory name
When I want to create a new payment method without factory name
And I name it "Payment method without gateway factory name" in "English (United States)"
@ -117,7 +117,7 @@ Feature: Payment method validation
And I try to add it
Then I should be notified that I have to specify factory name
@no-ui @api
@api @no-ui
Scenario: Trying to add a new payment method with wrong factory name
When I want to create a new payment method with wrong factory name
And I name it "Payment method with wrong gateway factory name" in "English (United States)"
@ -125,7 +125,7 @@ Feature: Payment method validation
And I try to add it
Then I should be notified that I have to specify factory name that is available
@ui @api
@api @ui
Scenario: Trying to remove name from an existing payment method
When I want to modify the "Offline" payment method
And I remove its name from "English (United States)" translation

View file

@ -14,7 +14,7 @@ Feature: Prevent deletion of used payment method
And the customer chose "DHL Express" shipping method to "United States" with "Cash on Delivery" payment
And I am logged in as an administrator
@ui @api
@api @ui
Scenario: Being unable to delete a payment method which is in use
When I try to delete the "Cash on Delivery" payment method
Then I should be notified that it is in use

View file

@ -7,37 +7,37 @@ Feature: Sorting listed payment methods
Background:
Given the store operates on a single channel in "United States"
And that channel allows to shop using "English (United States)" and "Polish (Poland)" locales
And the store has a payment method "PayPal Express Checkout" with a code "PAYPAL" and "Paypal Express Checkout" gateway
And the store has a payment method "PayPal Express Checkout" with a code "paypal" and "Paypal Express Checkout" gateway
And this payment method is named "Ekspresowy Paypal" in the "Polish (Poland)" locale
And the store has a payment method "Offline" with a code "Offline"
And the store has a payment method "Offline" with a code "offline"
And this payment method is named "Płatność Offline" in the "Polish (Poland)" locale
And the store has a payment method "Cash on Delivery" with a code "cash_on_delivery"
And this payment method is named "Płatność Przy Odbiorze" in the "Polish (Poland)" locale
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Payment methods can be sorted by their codes
Given I am browsing payment methods
When I start sorting payment methods by code
Then I should see 3 payment methods in the list
And the first payment method on the list should have code "cash_on_delivery"
@todo @ui @api
@api @ui
Scenario: Changing the order of sorting payment methods by their codes
Given I am browsing payment methods
And the payment methods are already sorted by code
When I switch the way payment methods are sorted to descending by code
Then I should see 3 payment methods in the list
And the first payment method on the list should have code "PAYPAL"
And the first payment method on the list should have code "paypal"
@todo @ui @api
@api @ui
Scenario: Payment methods can be sorted by their names
Given I am browsing payment methods
When I start sorting payment methods by name
Then I should see 3 payment methods in the list
And the first payment method on the list should have name "Cash on Delivery"
@todo @ui @api
@api @ui
Scenario: Changing the order of sorting payment methods by their names
Given I am browsing payment methods
And the payment methods are already sorted by name
@ -45,7 +45,7 @@ Feature: Sorting listed payment methods
Then I should see 3 payment methods in the list
And the first payment method on the list should have name "PayPal Express Checkout"
@todo @ui @api
@api @ui
Scenario: Payment methods are always sorted in the default locale
Given I change my locale to "Polish (Poland)"
And I am browsing payment methods

View file

@ -11,20 +11,20 @@ Feature: Sorting listed payment methods by position
And the store allows paying with "Offline" at position 1
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Payment methods are sorted by position in ascending order by default
When I browse payment methods
Then I should see 3 payment methods in the list
And the first payment method on the list should have name "Paypal Express Checkout"
And the last payment method on the list should have name "Cash on Delivery"
@todo @ui @api
@api @ui
Scenario: Payment method added at no position is added as the last one
Given the store allows paying with "Credit Card"
When I browse payment methods
Then the last payment method on the list should have name "Credit Card"
@todo @ui @api
@api @ui
Scenario: Payment method added at position 0 is added as the first one
Given the store also allows paying with "Credit Card" at position 0
When I browse payment methods

View file

@ -105,7 +105,7 @@ Feature: Creating a catalog promotion
And it should have "winter_sale" code and "Winter sale" name
And the "Winter sale" catalog promotion should apply to all variants of "T-Shirt" product
@api @todo-ui @mink:chromedriver
@api @ui @mink:chromedriver
Scenario: Creating a catalog promotion with start and end date
When I want to create a new catalog promotion
And I specify its code as "winter_sale"

View file

@ -7,7 +7,7 @@ Feature: Adding a new tax category
Background:
Given I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Adding a new tax category
When I want to create a new tax category
And I specify its code as "food_and_beverage"
@ -16,7 +16,7 @@ Feature: Adding a new tax category
Then I should be notified that it has been successfully created
And the tax category "Food and Beverage" should appear in the registry
@todo @ui @api
@api @ui
Scenario: Adding a new tax category with a description
When I want to create a new tax category
And I specify its code as "food_and_beverage"

View file

@ -8,7 +8,7 @@ Feature: Deleting multiple tax categories
Given the store has tax categories "Alcohol", "Food" and "Books"
And I am logged in as an administrator
@todo @ui @mink:chromedriver @no-api
@no-api @ui @mink:chromedriver @no-api
Scenario: Deleting multiple tax categories at once
When I browse tax categories
And I check the "Alcohol" tax category

View file

@ -8,7 +8,7 @@ Feature: Deleting a tax category
Given the store has a tax category "Alcohol" with a code "alcohol"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Deleted tax category should disappear from the registry
When I delete tax category "Alcohol"
Then I should be notified that it has been successfully deleted

View file

@ -8,12 +8,12 @@ Feature: Editing tax category
Given the store has a tax category "Alcohol" with a code "alcohol"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Inability of changing the code of an existing tax category
When I want to modify a tax category "Alcohol"
Then I should not be able to edit its code
@todo @ui @api
@api @ui
Scenario: Renaming the tax category
When I want to modify a tax category "Alcohol"
And I rename it to "Food & Alcohol"

View file

@ -0,0 +1,24 @@
@managing_tax_categories
Feature: Filtering tax categories
In order to quickly find the tax category I need
As an Administrator
I want to filter available tax categories
Background:
Given the store has a tax category "Alcohol" with a code "alcohol"
And the store has a tax category "Food" with a code "food"
And the store has a tax category "Seafood" with a code "seafood"
And I am logged in as an administrator
And I am browsing tax categories
@todo-api @ui
Scenario: Filtering tax categories by name
When I search by "Food" name
Then I should see 2 tax categories in the list
But I should not see the tax category "Alcohol"
@todo-api @ui
Scenario: Filtering tax categories by code
When I search by "alcohol" code
Then I should see a single tax category in the list
And I should see the tax category "Alcohol"

View file

@ -8,7 +8,7 @@ Feature: Tax category unique code validation
Given the store has a tax category "Alcohol" with a code "alcohol"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Trying to add tax category with taken code
When I want to create a new tax category
And I name it "Food and Beverage"

View file

@ -7,7 +7,7 @@ Feature: Tax category validation
Background:
Given I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Trying to add a new tax category without specifying its code
When I want to create a new tax category
And I name it "Food and Beverage"
@ -16,7 +16,7 @@ Feature: Tax category validation
Then I should be notified that code is required
And tax category with name "Food and Beverage" should not be added
@todo @ui @api
@api @ui
Scenario: Trying to add a new tax category with a too long code
When I want to create a new tax category
And I name it "Food and Beverage"
@ -24,7 +24,7 @@ Feature: Tax category validation
And I try to add it
Then I should be notified that code is too long
@todo @ui @api
@api @ui
Scenario: Trying to add a new tax category without specifying its name
When I want to create a new tax category
And I specify its code as "food_and_beverage"
@ -33,7 +33,16 @@ Feature: Tax category validation
Then I should be notified that name is required
And tax category with code "food_and_beverage" should not be added
@todo @ui @api
@api @ui
Scenario: Trying to add a new tax category with a too long name
When I want to create a new tax category
And I name it "Food and Beverage"
And I specify a too long name
And I specify its code as "food_and_beverage"
And I try to add it
Then I should be notified that name is too long
@api @ui
Scenario: Trying to remove name from existing tax category
Given the store has a tax category "Alcoholic Drinks" with a code "alcohol"
When I want to modify this tax category

View file

@ -7,7 +7,7 @@ Feature: Adding a new customer group
Background:
Given I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Adding a new customer group
When I want to create a new customer group
And I specify its code as "RETAIL"

View file

@ -8,7 +8,7 @@ Feature: Customer group unique code validation
Given the store has a customer group "Retail" with "RETAIL" code
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Trying to add a new customer group with used code
When I want to create a new customer group
And I specify its code as "RETAIL"

View file

@ -7,24 +7,24 @@ Feature: Customer group validation
Background:
Given I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Trying to add a new customer group without a name
When I want to create a new customer group
And I try to add it
Then I should be notified that name is required
And I should be informed that this form contains errors
# And I should be informed that this form contains errors #TODO: Uncomment when flash messages are brought back
@todo @ui @api
@api @ui
Scenario: Trying to remove name from an existing customer group
Given the store has a customer group "Retail"
When I want to edit this customer group
And I remove its name
And I try to save my changes
Then I should be notified that name is required
And I should be informed that this form contains errors
# And I should be informed that this form contains errors #TODO: Uncomment when flash messages are brought back
And this customer group should still be named "Retail"
@ui @api
@api @ui
Scenario: Adding a new customer group with a too long code
Given I want to create a new customer group
And I specify its name as "Retail"

View file

@ -8,7 +8,7 @@ Feature: Deleting customer groups
Given the store has a customer group "Retail"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Deleting a customer group
When I delete the "Retail" customer group
Then I should be notified that it has been successfully deleted

View file

@ -8,7 +8,7 @@ Feature: Editing a customer group
Given the store has a customer group "Retail"
And I am logged in as an administrator
@todo @ui @api
@api @ui
Scenario: Changing name of an existing customer group
When I want to edit this customer group
And I specify its name as "Wholesale"
@ -16,7 +16,7 @@ Feature: Editing a customer group
Then I should be notified that it has been successfully edited
And this customer group with name "Wholesale" should appear in the store
@todo @ui @api
@api @ui
Scenario: Seeing disabled code field while editing customer group
When I want to edit this customer group
Then I should not be able to edit its code

View file

@ -19,7 +19,7 @@ use Sylius\Behat\Client\ResponseCheckerInterface;
use Sylius\Behat\Context\Api\Resources;
use Webmozart\Assert\Assert;
final class ManagingCurrenciesContext implements Context
final readonly class ManagingCurrenciesContext implements Context
{
public function __construct(
private ApiClientInterface $client,
@ -62,7 +62,7 @@ final class ManagingCurrenciesContext implements Context
}
/**
* @Then I should see :count currencies in the list
* @Then I should see :count currencies on the list
*/
public function iShouldSeeCurrenciesInTheList(int $count): void
{
@ -72,7 +72,7 @@ final class ManagingCurrenciesContext implements Context
}
/**
* @Then I should see the currency :currencyName in the list
* @Then I should see the currency :currencyName on the list
* @Then the currency :currencyName should appear in the store
*/
public function currencyShouldAppearInTheStore(string $currencyName): void

View file

@ -418,7 +418,7 @@ final readonly class ManagingPaymentMethodsContext implements Context
{
$response = $this->client->index(Resources::PAYMENT_METHODS);
if ($field === 'Name') {
if ($field === 'name') {
$paymentMethods = $this->responseChecker->getCollection($response);
Assert::same(end($paymentMethods)['translations']['en_US']['name'], $value);

View file

@ -128,7 +128,7 @@ final readonly class PaymentContext implements Context
/**
* @Given the payment method :paymentMethod is disabled
* @Given /^(this payment method) has been disabled$/
* @Given /^(this payment method) (?:has been|is) disabled$/
* @When the payment method :paymentMethod gets disabled
*/
public function theStoreHasAPaymentMethodDisabled(PaymentMethodInterface $paymentMethod)

View file

@ -14,25 +14,25 @@ declare(strict_types=1);
namespace Sylius\Behat\Context\Ui\Admin;
use Behat\Behat\Context\Context;
use Sylius\Behat\Page\Admin\Currency\CreatePageInterface;
use Sylius\Behat\Element\Admin\Currency\FormElementInterface;
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface;
use Sylius\Behat\Page\Admin\Currency\IndexPageInterface;
use Sylius\Behat\Page\Admin\Currency\UpdatePageInterface;
use Sylius\Component\Currency\Model\CurrencyInterface;
use Webmozart\Assert\Assert;
final class ManagingCurrenciesContext implements Context
final readonly class ManagingCurrenciesContext implements Context
{
public function __construct(
private IndexPageInterface $indexPage,
private CreatePageInterface $createPage,
private UpdatePageInterface $updatePage,
private FormElementInterface $formElement,
) {
}
/**
* @When I want to add a new currency
*/
public function iWantToAddNewCurrency()
public function iWantToAddNewCurrency(): void
{
$this->createPage->open();
}
@ -40,103 +40,52 @@ final class ManagingCurrenciesContext implements Context
/**
* @When I choose :currencyName
*/
public function iChoose($currencyName)
public function iChoose($currencyName): void
{
$this->createPage->chooseName($currencyName);
$this->formElement->chooseCurrency($currencyName);
}
/**
* @When I add it
* @When I try to add it
*/
public function iAddIt()
public function iAddIt(): void
{
$this->createPage->create();
}
/**
* @Then the currency :currency should appear in the store
* @Then I should see the currency :currency in the list
* @Then I should see the currency :currency on the list
*/
public function currencyShouldAppearInTheStore(CurrencyInterface $currency)
public function currencyShouldAppearInTheStore(CurrencyInterface $currency): void
{
$this->indexPage->open();
Assert::true($this->indexPage->isSingleResourceOnPage(['code' => $currency->getCode()]));
}
/**
* @When /^I want to edit (this currency)$/
*/
public function iWantToEditThisCurrency(CurrencyInterface $currency)
{
$this->updatePage->open(['id' => $currency->getId()]);
}
/**
* @When I enable it
*/
public function iEnableIt()
{
$this->updatePage->enable();
}
/**
* @When I disable it
*/
public function iDisableIt()
{
$this->updatePage->disable();
}
/**
* @When I save my changes
* @When I try to save my changes
*/
public function iSaveMyChanges()
{
$this->updatePage->saveChanges();
}
/**
* @Then the code field should be disabled
*/
public function theCodeFiledShouldBeDisabled()
{
Assert::same($this->updatePage->getCodeDisabledAttribute(), 'disabled');
}
/**
* @Then I should be notified that currency code must be unique
*/
public function iShouldBeNotifiedThatCurrencyCodeMustBeUnique()
{
Assert::same($this->createPage->getValidationMessage('code'), 'Currency code must be unique.');
}
/**
* @Then there should still be only one currency with :element :code
*/
public function thereShouldStillBeOnlyOneCurrencyWithCode($element, $codeValue)
{
$this->indexPage->open();
Assert::true($this->indexPage->isSingleResourceOnPage([$element => $codeValue]));
}
/**
* @When I want to browse currencies of the store
*/
public function iWantToSeeAllCurrenciesInStore()
public function iWantToSeeAllCurrenciesInStore(): void
{
$this->indexPage->open();
}
/**
* @Then /^I should see (\d+) currencies in the list$/
* @Then /^I should see (\d+) currencies on the list$/
*/
public function iShouldSeeCurrenciesInTheList($amountOfCurrencies)
public function iShouldSeeCurrenciesInTheList(int $amountOfCurrencies): void
{
Assert::same($this->indexPage->countItems(), (int) $amountOfCurrencies);
Assert::same($this->indexPage->countItems(), $amountOfCurrencies);
}
/**
* @Then I should not be able to choose :name
*/
public function iShouldNotBeAbleToChoose(string $name): void
{
Assert::false($this->formElement->isCurrencyAvailable($name));
}
}

View file

@ -23,7 +23,7 @@ use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
use Sylius\Component\Customer\Model\CustomerGroupInterface;
use Webmozart\Assert\Assert;
final class ManagingCustomerGroupsContext implements Context
final readonly class ManagingCustomerGroupsContext implements Context
{
use ValidationTrait;
@ -38,7 +38,7 @@ final class ManagingCustomerGroupsContext implements Context
/**
* @When I want to create a new customer group
*/
public function iWantToCreateANewCustomerGroup()
public function iWantToCreateANewCustomerGroup(): void
{
$this->createPage->open();
}
@ -56,7 +56,7 @@ final class ManagingCustomerGroupsContext implements Context
* @When I specify its name as :name
* @When I remove its name
*/
public function iSpecifyItsNameAs($name = null)
public function iSpecifyItsNameAs(string $name = null): void
{
$this->createPage->nameIt($name ?? '');
}
@ -65,7 +65,7 @@ final class ManagingCustomerGroupsContext implements Context
* @When I add it
* @When I try to add it
*/
public function iAddIt()
public function iAddIt(): void
{
$this->createPage->create();
}
@ -73,7 +73,7 @@ final class ManagingCustomerGroupsContext implements Context
/**
* @Then the customer group :customerGroup should appear in the store
*/
public function theCustomerGroupShouldAppearInTheStore(CustomerGroupInterface $customerGroup)
public function theCustomerGroupShouldAppearInTheStore(CustomerGroupInterface $customerGroup): void
{
$this->indexPage->open();
@ -83,7 +83,7 @@ final class ManagingCustomerGroupsContext implements Context
/**
* @When /^I want to edit (this customer group)$/
*/
public function iWantToEditThisCustomerGroup(CustomerGroupInterface $customerGroup)
public function iWantToEditThisCustomerGroup(CustomerGroupInterface $customerGroup): void
{
$this->updatePage->open(['id' => $customerGroup->getId()]);
}
@ -92,7 +92,7 @@ final class ManagingCustomerGroupsContext implements Context
* @When I save my changes
* @When I try to save my changes
*/
public function iSaveMyChanges()
public function iSaveMyChanges(): void
{
$this->updatePage->saveChanges();
}
@ -117,7 +117,7 @@ final class ManagingCustomerGroupsContext implements Context
* @Then this customer group with name :name should appear in the store
* @Then I should see the customer group :name in the list
*/
public function thisCustomerGroupWithNameShouldAppearInTheStore($name)
public function thisCustomerGroupWithNameShouldAppearInTheStore(string $name): void
{
$this->indexPage->open();
@ -128,7 +128,7 @@ final class ManagingCustomerGroupsContext implements Context
* @When I browse customer groups
* @When I want to browse customer groups
*/
public function iWantToBrowseCustomerGroups()
public function iWantToBrowseCustomerGroups(): void
{
$this->indexPage->open();
}
@ -141,23 +141,24 @@ final class ManagingCustomerGroupsContext implements Context
{
$this->indexPage->open();
Assert::same($this->indexPage->countItems(), (int) $amountOfCustomerGroups);
Assert::same($this->indexPage->countItems(), $amountOfCustomerGroups);
}
/**
* @Then /^(this customer group) should still be named "([^"]+)"$/
*/
public function thisCustomerGroupShouldStillBeNamed(CustomerGroupInterface $customerGroup, $customerGroupName)
public function thisCustomerGroupShouldStillBeNamed(CustomerGroupInterface $customerGroup, string $customerGroupName): void
{
$this->iWantToBrowseCustomerGroups();
Assert::same($customerGroup->getName(), $customerGroupName);
Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $customerGroupName]));
}
/**
* @Then I should be notified that name is required
*/
public function iShouldBeNotifiedThatNameIsRequired()
public function iShouldBeNotifiedThatNameIsRequired(): void
{
Assert::same(
$this->updatePage->getValidationMessage('name'),
@ -168,7 +169,7 @@ final class ManagingCustomerGroupsContext implements Context
/**
* @Then I should be notified that customer group with this code already exists
*/
public function iShouldBeNotifiedThatCustomerGroupWithThisCodeAlreadyExists()
public function iShouldBeNotifiedThatCustomerGroupWithThisCodeAlreadyExists(): void
{
Assert::same($this->createPage->getValidationMessage('code'), 'Customer group code has to be unique.');
}
@ -176,7 +177,7 @@ final class ManagingCustomerGroupsContext implements Context
/**
* @Then I should be informed that this form contains errors
*/
public function iShouldBeInformedThatThisFormContainsErrors()
public function iShouldBeInformedThatThisFormContainsErrors(): void
{
/** @var CreatePageInterface|UpdatePageInterface $currentPage */
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
@ -195,7 +196,7 @@ final class ManagingCustomerGroupsContext implements Context
/**
* @When I delete the :customerGroup customer group
*/
public function iDeleteTheCustomerGroup(CustomerGroupInterface $customerGroup)
public function iDeleteTheCustomerGroup(CustomerGroupInterface $customerGroup): void
{
$this->iWantToBrowseCustomerGroups();
@ -205,7 +206,7 @@ final class ManagingCustomerGroupsContext implements Context
/**
* @Then /^(this customer group) should no longer exist in the registry$/
*/
public function thisCustomerGroupShouldNoLongerExistInTheRegistry(CustomerGroupInterface $customerGroup)
public function thisCustomerGroupShouldNoLongerExistInTheRegistry(CustomerGroupInterface $customerGroup): void
{
Assert::false(
$this->indexPage->isSingleResourceOnPage(['name' => $customerGroup->getName()]),

View file

@ -255,6 +255,22 @@ final readonly class ManagingPaymentMethodsContext implements Context
$this->indexPage->bulkDelete();
}
/**
* @Then I should see the payment method :paymentMethodName
*/
public function IShouldSeeThePaymentMethod(string $paymentMethodName): void
{
Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $paymentMethodName]));
}
/**
* @Then I should not see the payment method :paymentMethodName
*/
public function IShouldNotSeeThePaymentMethod(string $paymentMethodName): void
{
Assert::false($this->indexPage->isSingleResourceOnPage(['name' => $paymentMethodName]));
}
/**
* @Then the payment method :paymentMethodName should appear in the registry
* @Then the payment method :paymentMethodName should be in the registry
@ -262,9 +278,7 @@ final readonly class ManagingPaymentMethodsContext implements Context
*/
public function thePaymentMethodShouldAppearInTheRegistry(string $paymentMethodName): void
{
$this->indexPage->open();
Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $paymentMethodName]));
$this->thereShouldStillBeOnlyOnePaymentMethodWith('name', $paymentMethodName);
}
/**

View file

@ -14,48 +14,28 @@ declare(strict_types=1);
namespace Sylius\Behat\Context\Ui\Admin;
use Behat\Behat\Context\Context;
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPageInterface;
use Sylius\Behat\Context\Ui\Admin\Helper\ValidationTrait;
use Sylius\Behat\Element\Admin\TaxCategory\FormElementInterface;
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface;
use Sylius\Behat\Page\Admin\Crud\IndexPageInterface;
use Sylius\Behat\Page\Admin\TaxCategory\CreatePageInterface;
use Sylius\Behat\Page\Admin\TaxCategory\UpdatePageInterface;
use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface;
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
use Webmozart\Assert\Assert;
final class ManagingTaxCategoriesContext implements Context
final readonly class ManagingTaxCategoriesContext implements Context
{
use ValidationTrait;
public function __construct(
private IndexPageInterface $indexPage,
private CreatePageInterface $createPage,
private UpdatePageInterface $updatePage,
private CurrentPageResolverInterface $currentPageResolver,
private FormElementInterface $formElement,
) {
}
/**
* @When I delete tax category :taxCategory
*/
public function iDeletedTaxCategory(TaxCategoryInterface $taxCategory)
{
$this->indexPage->open();
$this->indexPage->deleteResourceOnPage(['code' => $taxCategory->getCode()]);
}
/**
* @Then /^(this tax category) should no longer exist in the registry$/
*/
public function thisTaxCategoryShouldNoLongerExistInTheRegistry(TaxCategoryInterface $taxCategory)
{
Assert::false($this->indexPage->isSingleResourceOnPage(['code' => $taxCategory->getCode()]));
}
/**
* @When I want to create a new tax category
*/
public function iWantToCreateNewTaxCategory()
public function iWantToCreateNewTaxCategory(): void
{
$this->createPage->open();
}
@ -66,7 +46,15 @@ final class ManagingTaxCategoriesContext implements Context
*/
public function iSpecifyItsCodeAs(?string $code = null): void
{
$this->createPage->specifyCode($code ?? '');
$this->formElement->setCode($code ?? '');
}
/**
* @When I specify a too long :field
*/
public function iSpecifyATooLongCode(string $field): void
{
$this->formElement->fillElement(str_repeat('a', 256), $field);
}
/**
@ -75,43 +63,33 @@ final class ManagingTaxCategoriesContext implements Context
* @When I do not name it
* @When I remove its name
*/
public function iNameIt($name = null)
public function iNameIt($name = null): void
{
$this->createPage->nameIt($name ?? '');
$this->formElement->setName($name ?? '');
}
/**
* @When I describe it as :description
*/
public function iDescribeItAs($description): void
{
$this->formElement->setDescription($description);
}
/**
* @When I add it
* @When I try to add it
*/
public function iAddIt()
public function iAddIt(): void
{
$this->createPage->create();
}
/**
* @Then I should see the tax category :taxCategoryName in the list
* @Then the tax category :taxCategoryName should appear in the registry
*/
public function theTaxCategoryShouldAppearInTheRegistry(string $taxCategoryName): void
{
$this->indexPage->open();
Assert::true($this->indexPage->isSingleResourceOnPage(['nameAndDescription' => $taxCategoryName]));
}
/**
* @When I describe it as :description
*/
public function iDescribeItAs($description)
{
$this->createPage->describeItAs($description);
}
/**
* @When I want to modify a tax category :taxCategory
* @When /^I want to modify (this tax category)$/
*/
public function iWantToModifyTaxCategory(TaxCategoryInterface $taxCategory)
public function iWantToModifyTaxCategory(TaxCategoryInterface $taxCategory): void
{
$this->updatePage->open(['id' => $taxCategory->getId()]);
}
@ -120,12 +98,13 @@ final class ManagingTaxCategoriesContext implements Context
* @When I save my changes
* @When I try to save my changes
*/
public function iSaveMyChanges()
public function iSaveMyChanges(): void
{
$this->updatePage->saveChanges();
}
/**
* @Given I am browsing tax categories
* @When I browse tax categories
*/
public function iWantToBrowseTaxCategories(): void
@ -149,19 +128,46 @@ final class ManagingTaxCategoriesContext implements Context
$this->indexPage->bulkDelete();
}
/**
* @When I delete tax category :taxCategory
*/
public function iDeletedTaxCategory(TaxCategoryInterface $taxCategory): void
{
$this->indexPage->open();
$this->indexPage->deleteResourceOnPage(['code' => $taxCategory->getCode()]);
}
/**
* @Then /^(this tax category) should no longer exist in the registry$/
*/
public function thisTaxCategoryShouldNoLongerExistInTheRegistry(TaxCategoryInterface $taxCategory): void
{
Assert::false($this->indexPage->isSingleResourceOnPage(['code' => $taxCategory->getCode()]));
}
/**
* @Then I should see the tax category :taxCategoryName in the list
* @Then the tax category :taxCategoryName should appear in the registry
*/
public function theTaxCategoryShouldAppearInTheRegistry(string $taxCategoryName): void
{
$this->indexPage->open();
Assert::true($this->indexPage->isSingleResourceOnPage(['nameAndDescription' => $taxCategoryName]));
}
/**
* @Then I should not be able to edit its code
*/
public function iShouldNotBeAbleToEditItsCode(): void
{
Assert::true($this->updatePage->isCodeDisabled());
Assert::true($this->formElement->isCodeDisabled());
}
/**
* @Then /^(this tax category) name should be "([^"]+)"$/
* @Then /^(this tax category) should still be named "([^"]+)"$/
*/
public function thisTaxCategoryNameShouldBe(TaxCategoryInterface $taxCategory, $taxCategoryName)
public function thisTaxCategoryNameShouldBe(TaxCategoryInterface $taxCategory, $taxCategoryName): void
{
$this->indexPage->open();
Assert::true($this->indexPage->isSingleResourceOnPage(['code' => $taxCategory->getCode(), 'nameAndDescription' => $taxCategoryName]));
@ -170,15 +176,15 @@ final class ManagingTaxCategoriesContext implements Context
/**
* @Then I should be notified that tax category with this code already exists
*/
public function iShouldBeNotifiedThatTaxCategoryWithThisCodeAlreadyExists()
public function iShouldBeNotifiedThatTaxCategoryWithThisCodeAlreadyExists(): void
{
Assert::same($this->createPage->getValidationMessage('code'), 'The tax category with given code already exists.');
Assert::same($this->formElement->getValidationMessage('code'), 'The tax category with given code already exists.');
}
/**
* @Then there should still be only one tax category with :element :code
*/
public function thereShouldStillBeOnlyOneTaxCategoryWith($element, $code)
public function thereShouldStillBeOnlyOneTaxCategoryWith($element, $code): void
{
$this->indexPage->open();
Assert::true($this->indexPage->isSingleResourceOnPage([$element => $code]));
@ -187,18 +193,15 @@ final class ManagingTaxCategoriesContext implements Context
/**
* @Then I should be notified that :element is required
*/
public function iShouldBeNotifiedThatIsRequired($element)
public function iShouldBeNotifiedThatIsRequired($element): void
{
/** @var CreatePageInterface|UpdatePageInterface $currentPage */
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
Assert::same($currentPage->getValidationMessage($element), sprintf('Please enter tax category %s.', $element));
Assert::same($this->formElement->getValidationMessage($element), sprintf('Please enter tax category %s.', $element));
}
/**
* @Then tax category with :element :name should not be added
*/
public function taxCategoryWithElementValueShouldNotBeAdded($element, $name)
public function taxCategoryWithElementValueShouldNotBeAdded($element, $name): void
{
$this->indexPage->open();
Assert::false($this->indexPage->isSingleResourceOnPage([$element => $name]));
@ -206,14 +209,37 @@ final class ManagingTaxCategoriesContext implements Context
/**
* @Then I should see a single tax category in the list
* @Then I should see :amount tax categories in the list
*/
public function iShouldSeeTaxCategoriesInTheList(): void
public function iShouldSeeTaxCategoriesInTheList(int $amount = 1): void
{
Assert::same($this->indexPage->countItems(), 1);
Assert::same($this->indexPage->countItems(), $amount);
}
protected function resolveCurrentPage(): SymfonyPageInterface
/**
* @Then I should see the tax category :taxCategoryName
*/
public function IShouldSeeTheTaxCategory(string $taxCategoryName): void
{
return $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
Assert::true($this->indexPage->isSingleResourceOnPage(['nameAndDescription' => $taxCategoryName]));
}
/**
* @Then I should not see the tax category :taxCategoryName
*/
public function IShouldNotSeeTheTaxCategory(string $taxCategoryName): void
{
Assert::false($this->indexPage->isSingleResourceOnPage(['nameAndDescription' => $taxCategoryName]));
}
/**
* @Then I should be notified that :field is too long
*/
public function iShouldBeNotifiedThatIsTooLong(string $field): void
{
Assert::contains(
$this->formElement->getValidationMessage($field),
'must not be longer than 255 characters.',
);
}
}

View file

@ -83,6 +83,7 @@ final class FormElement extends BaseFormElement implements FormElementInterface
$this->getElement('end_date_date')->setValue(date('Y-m-d', $timestamp));
$this->getElement('end_date_time')->setValue(date('H:i', $timestamp));
$this->waitForFormUpdate();
}
public function addScope(string $type): void
@ -210,6 +211,7 @@ final class FormElement extends BaseFormElement implements FormElementInterface
'end_date_date' => '#sylius_admin_catalog_promotion_endDate_date',
'end_date_time' => '#sylius_admin_catalog_promotion_endDate_time',
'exclusive' => '#sylius_admin_catalog_promotion_exclusive',
'form' => '[data-live-name-value="sylius_admin:catalog_promotion:form"]',
'label' => '[name="sylius_admin_catalog_promotion[translations][%locale_code%][label]"]',
'last_action' => '[data-test-actions] [data-test-entry-row]:last-child',
'last_scope' => '[data-test-scopes] [data-test-entry-row]:last-child',

View file

@ -21,6 +21,12 @@ class FormElement extends Element implements FormElementInterface
{
private ?NodeElement $form = null;
public function fillElement(string $value, string $element, array $parameters = []): void
{
$foundElement = $this->getElement($element, $parameters);
$foundElement->setValue($value);
}
/**
* @param array<string, string> $parameters
*/

View file

@ -17,6 +17,11 @@ use Behat\Mink\Exception\ElementNotFoundException;
interface FormElementInterface
{
/**
* @param array<string, string> $parameters
*/
public function fillElement(string $value, string $element, array $parameters = []): void;
/**
* @param array<string, string> $parameters
*/

View file

@ -0,0 +1,64 @@
<?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\Currency;
use Behat\Mink\Session;
use FriendsOfBehat\SymfonyExtension\Mink\MinkParameters;
use Sylius\Behat\Element\Admin\Crud\FormElement as BaseFormElement;
use Sylius\Behat\Service\Helper\AutocompleteHelperInterface;
final class FormElement extends BaseFormElement implements FormElementInterface
{
public function __construct(
Session $session,
array|MinkParameters $minkParameters,
private readonly AutocompleteHelperInterface $autocompleteHelper,
) {
parent::__construct($session, $minkParameters);
}
public function chooseCurrency(string $currencyName): void
{
$this->autocompleteHelper->selectByName(
$this->getDriver(),
$this->getElement('code')->getXpath(),
$currencyName,
);
}
public function isCurrencyAvailable(string $currencyName): bool
{
$elements = $this->autocompleteHelper->search(
$this->getDriver(),
$this->getElement('code')->getXpath(),
$currencyName,
);
foreach ($elements as $element) {
if ($element === $currencyName) {
return true;
}
}
return false;
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'code' => '[data-test-code]',
]);
}
}

View file

@ -0,0 +1,21 @@
<?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\Currency;
interface FormElementInterface
{
public function chooseCurrency(string $currencyName): void;
public function isCurrencyAvailable(string $currencyName): bool;
}

View file

@ -0,0 +1,48 @@
<?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\TaxCategory;
use Sylius\Behat\Element\Admin\Crud\FormElement as BaseFormElement;
final class FormElement extends BaseFormElement implements FormElementInterface
{
public function setCode(string $code): void
{
$this->getElement('code')->setValue($code);
}
public function isCodeDisabled(): bool
{
return $this->getElement('code')->hasAttribute('disabled');
}
public function setName(string $name): void
{
$this->getElement('name')->setValue($name);
}
public function setDescription(string $description): void
{
$this->getElement('description')->setValue($description);
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'code' => '[data-test-code]',
'description' => '[data-test-description]',
'name' => '[data-test-name]',
]);
}
}

View file

@ -0,0 +1,27 @@
<?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\TaxCategory;
use Sylius\Behat\Element\Admin\Crud\FormElementInterface as BaseFormElementInterface;
interface FormElementInterface extends BaseFormElementInterface
{
public function setCode(string $code): void;
public function isCodeDisabled(): bool;
public function setName(string $name): void;
public function setDescription(string $description): void;
}

View file

@ -1,65 +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 Behat\Mink\Element\NodeElement;
use Sylius\Behat\Behaviour\Toggles;
use Sylius\Behat\Page\Admin\Crud\UpdatePage as BaseUpdatePage;
class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
{
use Toggles;
public function canBeDisabled(): bool
{
$toggleableElement = $this->getToggleableElement();
$this->assertCheckboxState($toggleableElement, true);
return $toggleableElement->hasAttribute('disabled');
}
public function canHaveExchangeRateChanged(): bool
{
return $this->getElement('exchangeRate')->hasAttribute('disabled');
}
public function changeExchangeRate(string $exchangeRate): void
{
$this->getDocument()->fillField('Exchange rate', $exchangeRate);
}
public function getExchangeRateValue(): string
{
return $this->getElement('exchangeRate')->getValue();
}
public function getCodeDisabledAttribute(): string
{
return $this->getElement('code')->getAttribute('disabled');
}
protected function getToggleableElement(): NodeElement
{
return $this->getElement('enabled');
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'code' => '#sylius_admin_currency_code',
'enabled' => '#sylius_admin_currency_enabled',
'exchangeRate' => '#sylius_admin_currency_exchangeRate',
]);
}
}

View file

@ -1,33 +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\UpdatePageInterface as BaseUpdatePageInterface;
interface UpdatePageInterface extends BaseUpdatePageInterface
{
public function enable(): void;
public function disable(): void;
public function canBeDisabled(): bool;
public function canHaveExchangeRateChanged(): bool;
public function changeExchangeRate(string $exchangeRate): void;
public function getExchangeRateValue(): string;
public function getCodeDisabledAttribute(): string;
}

View file

@ -1,34 +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\TaxCategory;
use Sylius\Behat\Behaviour\DescribesIt;
use Sylius\Behat\Behaviour\NamesIt;
use Sylius\Behat\Behaviour\SpecifiesItsField;
use Sylius\Behat\Page\Admin\Crud\CreatePage as BaseCreatePage;
class CreatePage extends BaseCreatePage implements CreatePageInterface
{
use SpecifiesItsField;
use NamesIt;
use DescribesIt;
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'code' => '#sylius_admin_tax_category_code',
'name' => '#sylius_admin_tax_category_name',
]);
}
}

View file

@ -1,25 +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\TaxCategory;
use Sylius\Behat\Page\Admin\Crud\CreatePageInterface as BaseCreatePageInterface;
interface CreatePageInterface extends BaseCreatePageInterface
{
public function specifyCode(string $code): void;
public function nameIt(string $name): void;
public function describeItAs(string $description): void;
}

View file

@ -1,39 +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\TaxCategory;
use Behat\Mink\Element\NodeElement;
use Sylius\Behat\Behaviour\ChecksCodeImmutability;
use Sylius\Behat\Behaviour\NamesIt;
use Sylius\Behat\Page\Admin\Crud\UpdatePage as BaseUpdatePage;
class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
{
use ChecksCodeImmutability;
use NamesIt;
protected function getCodeElement(): NodeElement
{
return $this->getElement('code');
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'code' => '#sylius_admin_tax_category_code',
'description' => '#sylius_admin_tax_category_description',
'name' => '#sylius_admin_tax_category_name',
]);
}
}

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\TaxCategory;
use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface as BaseUpdatePageInterface;
interface UpdatePageInterface extends BaseUpdatePageInterface
{
public function isCodeDisabled(): bool;
public function nameIt(string $name): void;
}

View file

@ -92,7 +92,7 @@
<service id="sylius.behat.context.ui.admin.managing_currencies" class="Sylius\Behat\Context\Ui\Admin\ManagingCurrenciesContext">
<argument type="service" id="sylius.behat.page.admin.currency.index" />
<argument type="service" id="sylius.behat.page.admin.currency.create" />
<argument type="service" id="sylius.behat.page.admin.currency.update" />
<argument type="service" id="sylius.behat.element.admin.currency.form" />
</service>
<service id="sylius.behat.context.ui.admin.managing_customers" class="Sylius\Behat\Context\Ui\Admin\ManagingCustomersContext">
@ -297,7 +297,7 @@
<argument type="service" id="sylius.behat.page.admin.tax_category.index" />
<argument type="service" id="sylius.behat.page.admin.tax_category.create" />
<argument type="service" id="sylius.behat.page.admin.tax_category.update" />
<argument type="service" id="sylius.behat.current_page_resolver" />
<argument type="service" id="sylius.behat.element.admin.tax_category.form" />
</service>
<service id="sylius.behat.context.ui.admin.managing_translatable_entities" class="Sylius\Behat\Context\Ui\Admin\ManagingTranslatableEntitiesContext">

View file

@ -115,5 +115,19 @@
class="Sylius\Behat\Element\Admin\PromotionCoupon\FormElement"
parent="sylius.behat.element.admin.crud.form"
/>
<service
id="sylius.behat.element.admin.tax_category.form"
class="Sylius\Behat\Element\Admin\TaxCategory\FormElement"
parent="sylius.behat.element.admin.crud.form"
/>
<service
id="sylius.behat.element.admin.currency.form"
class="Sylius\Behat\Element\Admin\Currency\FormElement"
parent="sylius.behat.element.admin.crud.form"
>
<argument type="service" id="Sylius\Behat\Service\Helper\AutocompleteHelperInterface" />
</service>
</services>
</container>

View file

@ -13,9 +13,8 @@
<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.currency.create.class">Sylius\Behat\Page\Admin\Currency\CreatePage</parameter>
<parameter key="sylius.behat.page.admin.currency.create.class">%sylius.behat.page.admin.crud.create.class%</parameter>
<parameter key="sylius.behat.page.admin.currency.index.class">Sylius\Behat\Page\Admin\Currency\IndexPage</parameter>
<parameter key="sylius.behat.page.admin.currency.update.class">Sylius\Behat\Page\Admin\Currency\UpdatePage</parameter>
</parameters>
<services>
@ -27,8 +26,5 @@
<service id="sylius.behat.page.admin.currency.index" class="%sylius.behat.page.admin.currency.index.class%" parent="sylius.behat.page.admin.crud.index" public="false">
<argument type="string">sylius_admin_currency_index</argument>
</service>
<service id="sylius.behat.page.admin.currency.update" class="%sylius.behat.page.admin.currency.update.class%" parent="sylius.behat.page.admin.crud.update" public="false">
<argument type="string">sylius_admin_currency_update</argument>
</service>
</services>
</container>

View file

@ -13,9 +13,9 @@
<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.tax_category.create.class">Sylius\Behat\Page\Admin\TaxCategory\CreatePage</parameter>
<parameter key="sylius.behat.page.admin.tax_category.create.class">%sylius.behat.page.admin.crud.create.class%</parameter>
<parameter key="sylius.behat.page.admin.tax_category.index.class">%sylius.behat.page.admin.crud.index.class%</parameter>
<parameter key="sylius.behat.page.admin.tax_category.update.class">Sylius\Behat\Page\Admin\TaxCategory\UpdatePage</parameter>
<parameter key="sylius.behat.page.admin.tax_category.update.class">%sylius.behat.page.admin.crud.update.class%</parameter>
</parameters>
<services>

View file

@ -29,6 +29,7 @@ default:
- sylius.behat.context.ui.admin.managing_payment_methods
- sylius.behat.context.ui.admin.notification
- sylius.behat.context.ui.admin.search_filter
- sylius.behat.context.ui.shop.locale
filters:

View file

@ -16,6 +16,7 @@ default:
- sylius.behat.context.ui.admin.managing_tax_categories
- sylius.behat.context.ui.admin.notification
- sylius.behat.context.ui.admin.search_filter
filters:
tags: "@managing_tax_categories&&@ui"

View file

@ -14,10 +14,46 @@ declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Form\Type;
use Sylius\Bundle\CurrencyBundle\Form\Type\CurrencyType as BaseCurrencyType;
use Sylius\Component\Currency\Model\CurrencyInterface;
use Sylius\Component\Currency\Repository\CurrencyRepositoryInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CurrencyType as SymfonyCurrencyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Intl\Currencies;
final class CurrencyType extends AbstractType
{
/** @param CurrencyRepositoryInterface<CurrencyInterface> $currencyRepository */
public function __construct(private readonly CurrencyRepositoryInterface $currencyRepository)
{
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event): void {
$options = [
'label' => 'sylius.form.currency.code',
'choice_loader' => null,
'autocomplete' => true,
'placeholder' => 'sylius.form.currency.select',
];
$currency = $event->getData();
if ($currency instanceof CurrencyInterface && null !== $currency->getCode()) {
$options['disabled'] = true;
$options['choices'] = [Currencies::getName($currency->getCode()) => $currency->getCode()];
} else {
$options['choices'] = array_flip($this->getAvailableCurrencies());
}
$form = $event->getForm();
$form->add('code', SymfonyCurrencyType::class, $options);
});
}
public function getBlockPrefix(): string
{
return 'sylius_admin_currency';
@ -27,4 +63,21 @@ final class CurrencyType extends AbstractType
{
return BaseCurrencyType::class;
}
/**
* @return array<string, string>
*/
private function getAvailableCurrencies(): array
{
$availableCurrencies = Currencies::getNames();
/** @var CurrencyInterface[] $definedCurrencies */
$definedCurrencies = $this->currencyRepository->findAll();
foreach ($definedCurrencies as $currency) {
unset($availableCurrencies[$currency->getCode()]);
}
return $availableCurrencies;
}
}

View file

@ -0,0 +1,104 @@
<?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;
use Sylius\Component\Core\Model\AdminUserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
final readonly class LoggedInAdminUserProvider implements LoggedInAdminUserProviderInterface
{
private const SECURITY_SESSION_KEY = '_security_admin';
/** @param UserRepositoryInterface<AdminUserInterface> $adminUserRepository */
public function __construct(
private Security $security,
private TokenStorageInterface $tokenStorage,
private RequestStack $requestStack,
private UserRepositoryInterface $adminUserRepository,
) {
}
public function getUser(): ?AdminUserInterface
{
$user = $this->security->getUser();
if ($user instanceof AdminUserInterface) {
return $user;
}
$user = $this->getUserFromTokenStorage();
if (null !== $user) {
return $user;
}
return $this->getUserFromSession();
}
public function hasUser(): bool
{
return
$this->security->getUser() instanceof AdminUserInterface ||
null !== $this->getUserFromTokenStorage() ||
null !== $this->getSerializedTokenFromSession()
;
}
private function getUserFromTokenStorage(): ?AdminUserInterface
{
$user = $this->tokenStorage->getToken()?->getUser();
if ($user instanceof AdminUserInterface) {
return $user;
}
return null;
}
private function getUserFromSession(): ?AdminUserInterface
{
$serializedToken = $this->getSerializedTokenFromSession();
if (null === $serializedToken) {
return null;
}
$token = unserialize($serializedToken);
if (!$token instanceof TokenInterface) {
return null;
}
$user = $token->getUser();
if (!$user instanceof AdminUserInterface) {
return null;
}
return $this->adminUserRepository->find($user->getId());
}
private function getSerializedTokenFromSession(): ?string
{
try {
$serializedToken = $this->requestStack->getMainRequest()?->getSession()->get(self::SECURITY_SESSION_KEY);
if (null !== $serializedToken) {
return $serializedToken;
}
return $this->requestStack->getSession()->get(self::SECURITY_SESSION_KEY);
} catch (SessionNotFoundException) {
return null;
}
}
}

View file

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Provider;
use Sylius\Component\Core\Model\AdminUserInterface;
interface LoggedInAdminUserProviderInterface
{
public function hasUser(): bool;
public function getUser(): ?AdminUserInterface;
}

View file

@ -8,7 +8,7 @@
*/
import { Controller } from '@hotwired/stimulus';
import Modal from 'bootstrap/js/dist/modal';
import { Modal } from 'bootstrap';
export default class extends Controller {
static targets = ['modal', 'parent', 'csrfToken'];

View file

@ -14,6 +14,7 @@ import './bootstrap';
import './scripts/bulk-delete';
import './scripts/check-all';
import './scripts/choices';
import './scripts/menu-search';
import './scripts/statistics_chart';
import './scripts/sticky-header';

View file

@ -0,0 +1,70 @@
/*
* 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.
*/
/* global document */
(function() {
const menuSearchInput = document.querySelector('[data-menu-search]');
const menuClearButton = document.querySelector('[data-menu-search-clear]');
const clearInput = () => {
menuSearchInput.value = '';
menuSearchInput.dispatchEvent(new Event('input'));
};
if (menuSearchInput) {
menuSearchInput.addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
const navItems = document.querySelectorAll('.sidebar .nav-item');
navItems.forEach(navItem => {
const navLink = navItem.querySelector('.nav-link');
const dropdownMenu = navItem.querySelector('.dropdown-menu');
const dropdownItems = navItem.querySelectorAll('.dropdown-item');
let matchFound = false;
dropdownItems.forEach(item => {
const text = item.textContent.toLowerCase();
if (query === '' || text.includes(query)) {
item.style.display = '';
matchFound = true;
} else {
item.style.display = 'none';
}
});
if (matchFound || query === '') {
navItem.style.display = '';
} else {
navItem.style.display = 'none';
}
if (query !== '') {
if (navLink) navLink.classList.add('d-flex');
if (dropdownMenu) dropdownMenu.classList.add('d-flex');
} else {
if (navLink) navLink.classList.remove('d-flex');
if (dropdownMenu) dropdownMenu.classList.remove('d-flex');
}
});
});
menuSearchInput.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
clearInput();
}
});
}
if (menuClearButton) {
menuClearButton.addEventListener('click', function() {
clearInput();
});
}
})();

View file

@ -47,3 +47,52 @@
}
}
}
.menu-search {
.btn,
.form-control {
border: 1px solid transparent;
background: transparent;
transition: opacity 0.1s;
}
.btn {
padding-left: 0.4rem;
border-right: 0;
}
.form-control {
border-left: 0;
}
> * {
opacity: 0.5;
}
&:has(input[data-menu-search]:focus) {
> * {
opacity: 0.8;
}
.btn,
.form-control {
border-color: var(--tblr-navbar-border-color);
}
}
[data-menu-search-clear] {
display: none;
}
[data-menu-search-icon] {
display: inherit;
}
&:has(input[data-menu-search]:not(:placeholder-shown)) [data-menu-search-clear] {
display: inherit;
}
&:has(input[data-menu-search]:not(:placeholder-shown)) [data-menu-search-icon] {
display: none;
}
}

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.admin_user.create.content':
form:
template: '@SyliusAdmin/shared/crud/create/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.admin_user.update.content':
form:
template: '@SyliusAdmin/shared/crud/update/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -14,7 +14,7 @@ twig_hooks:
header:
template: '@SyliusAdmin/shared/crud/common/content/header.html.twig'
form:
template: '@SyliusAdmin/shared/crud/create/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: true
footer:

View file

@ -14,7 +14,7 @@ twig_hooks:
header:
template: '@SyliusAdmin/shared/crud/common/content/header.html.twig'
form:
template: '@SyliusAdmin/shared/crud/update/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: true
footer:

View file

@ -0,0 +1,19 @@
twig_hooks:
hooks:
'sylius_admin.currency.create.content':
form:
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false
'sylius_admin.currency.create.content.form':
sections:
template: '@SyliusAdmin/currency/form/sections.html.twig'
'sylius_admin.currency.create.content.form.sections':
general:
template: '@SyliusAdmin/currency/form/sections/general.html.twig'
'sylius_admin.currency.create.content.form.sections.general':
code:
template: '@SyliusAdmin/currency/form/sections/general/code.html.twig'

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.customer.create.content':
form:
template: '@SyliusAdmin/shared/crud/create/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.customer.update.content':
form:
template: '@SyliusAdmin/shared/crud/update/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -0,0 +1,24 @@
twig_hooks:
hooks:
'sylius_admin.customer_group.create.content':
form:
component: 'sylius_admin:customer_group:form'
props:
resource: '@=_context.resource'
form: '@=_context.form'
configuration:
render_rest: false
'sylius_admin.customer_group.create.content.form':
sections:
template: '@SyliusAdmin/customer_group/form/sections.html.twig'
'sylius_admin.customer_group.create.content.form.sections':
details:
template: '@SyliusAdmin/customer_group/form/sections/details.html.twig'
'sylius_admin.customer_group.create.content.form.sections.details':
code:
template: '@SyliusAdmin/customer_group/form/sections/details/code.html.twig'
name:
template: '@SyliusAdmin/customer_group/form/sections/details/name.html.twig'

View file

@ -0,0 +1,25 @@
twig_hooks:
hooks:
'sylius_admin.customer_group.update.content':
form:
component: 'sylius_admin:customer_group:form'
props:
resource: '@=_context.resource'
form: '@=_context.form'
configuration:
render_rest: false
method: 'PUT'
'sylius_admin.customer_group.update.content.form':
sections:
template: '@SyliusAdmin/customer_group/form/sections.html.twig'
'sylius_admin.customer_group.update.content.form.sections':
details:
template: '@SyliusAdmin/customer_group/form/sections/details.html.twig'
'sylius_admin.customer_group.update.content.form.sections.details':
code:
template: '@SyliusAdmin/customer_group/form/sections/details/code.html.twig'
name:
template: '@SyliusAdmin/customer_group/form/sections/details/name.html.twig'

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.product_association_type.create.content':
form:
template: '@SyliusAdmin/shared/crud/create/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.product_association_type.update.content':
form:
template: '@SyliusAdmin/shared/crud/update/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.product_review.update.content':
form:
template: '@SyliusAdmin/shared/crud/update/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.promotion_coupon.create.content':
form:
template: '@SyliusAdmin/shared/crud/create/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.promotion_coupon.update.content':
form:
template: '@SyliusAdmin/shared/crud/update/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -0,0 +1,23 @@
twig_hooks:
hooks:
'sylius_admin.tax_category.create.content':
form:
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false
'sylius_admin.tax_category.create.content.form':
sections:
template: '@SyliusAdmin/tax_category/form/sections.html.twig'
'sylius_admin.tax_category.create.content.form.sections':
details:
template: '@SyliusAdmin/tax_category/form/sections/details.html.twig'
'sylius_admin.tax_category.create.content.form.sections.details':
name:
template: '@SyliusAdmin/tax_category/form/sections/details/name.html.twig'
code:
template: '@SyliusAdmin/tax_category/form/sections/details/code.html.twig'
description:
template: '@SyliusAdmin/tax_category/form/sections/details/description.html.twig'

View file

@ -0,0 +1,23 @@
twig_hooks:
hooks:
'sylius_admin.tax_category.update.content':
form:
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false
'sylius_admin.tax_category.update.content.form':
sections:
template: '@SyliusAdmin/tax_category/form/sections.html.twig'
'sylius_admin.tax_category.update.content.form.sections':
details:
template: '@SyliusAdmin/tax_category/form/sections/details.html.twig'
'sylius_admin.tax_category.update.content.form.sections.details':
name:
template: '@SyliusAdmin/tax_category/form/sections/details/name.html.twig'
code:
template: '@SyliusAdmin/tax_category/form/sections/details/code.html.twig'
description:
template: '@SyliusAdmin/tax_category/form/sections/details/description.html.twig'

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.tax_rate.create.content':
form:
template: '@SyliusAdmin/shared/crud/create/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -2,7 +2,7 @@ twig_hooks:
hooks:
'sylius_admin.tax_rate.update.content':
form:
template: '@SyliusAdmin/shared/crud/update/content/form.html.twig'
template: '@SyliusAdmin/shared/crud/common/content/form.html.twig'
configuration:
render_rest: false

View file

@ -54,6 +54,8 @@ sylius_grid:
label: sylius.ui.search
options:
fields: [code, translation.name]
form_options:
type: !php/const Sylius\Component\Grid\Filter\StringFilter::TYPE_CONTAINS
enabled:
type: boolean
label: sylius.ui.enabled

View file

@ -27,6 +27,8 @@ sylius_grid:
label: sylius.ui.search
options:
fields: [code, name]
form_options:
type: !php/const Sylius\Component\Grid\Filter\StringFilter::TYPE_CONTAINS
actions:
main:
create:

View file

@ -9,11 +9,4 @@ sylius_admin_tax_rate:
form:
type: Sylius\Bundle\AdminBundle\Form\Type\TaxRateType
permission: true
vars:
all:
subheader: sylius.ui.manage_taxation_of_your_products
templates:
form: "@SyliusAdmin/TaxRate/_form.html.twig"
index:
icon: money
type: sylius.resource

View file

@ -121,5 +121,13 @@
<argument type="service" id="sylius.generator.taxon_slug" />
</service>
<service id="Sylius\Bundle\AdminBundle\Generator\TaxonSlugGeneratorInterface" alias="sylius_admin.generator.taxon_slug" />
<service id="sylius_admin.logged_in_admin_user_provider" class="Sylius\Bundle\AdminBundle\Provider\LoggedInAdminUserProvider">
<argument type="service" id="security.helper" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="request_stack" />
<argument type="service" id="sylius.repository.admin_user" />
</service>
<service id="Sylius\Bundle\AdminBundle\Provider\LoggedInAdminUserProviderInterface" alias="sylius_admin.logged_in_admin_user_provider" />
</services>
</container>

View file

@ -144,6 +144,7 @@
</service>
<service id="sylius_admin.form.type.currency" class="Sylius\Bundle\AdminBundle\Form\Type\CurrencyType" >
<argument type="service" id="sylius.repository.currency" />
<tag name="form.type" />
</service>

View file

@ -53,6 +53,20 @@
/>
</service>
<service
id="sylius_admin.twig.component.customer_group.form"
class="Sylius\Bundle\AdminBundle\Twig\Component\CustomerGroup\FormComponent"
>
<argument type="service" id="form.factory" />
<argument>Sylius\Bundle\AdminBundle\Form\Type\CustomerGroupType</argument>
<tag
name="sylius.live_component"
key="sylius_admin:customer_group:form"
template="@SyliusAdmin/shared/crud/common/content/form.html.twig"
/>
</service>
<service
id="sylius_admin.twig.component.product_option.form"
class="Sylius\Bundle\AdminBundle\Twig\Component\ProductOption\FormComponent"
@ -295,7 +309,7 @@
>
<argument type="service" id="router" />
<argument type="service" id="translator" />
<argument type="service" id="security.helper" />
<argument type="service" id="sylius_admin.logged_in_admin_user_provider" />
<tag
name="twig.component"

View file

@ -15,7 +15,7 @@
<services>
<service id="sylius_admin.twig.error_template_finder" class="Sylius\Bundle\AdminBundle\Twig\ErrorTemplateFinder\ErrorTemplateFinder">
<argument type="service" id="Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="sylius_admin.logged_in_admin_user_provider" />
<argument type="service" id="twig" />
<tag name="sylius.twig.error_template_finder" />
</service>

View file

@ -105,6 +105,7 @@ sylius:
product_not_active_in_channel: The product is not yet activated in this channel.
product_variant: Product variant
sales_summary: Sales summary
search_menu: 'Search menu'
shipment_for_order: Shipment for order
show_history_for_channel_pricing: 'Show history for channel pricing'
start_typing_to_search: 'Start typing to search'

View file

@ -0,0 +1,46 @@
<?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\Twig\Component\CustomerGroup;
use Sylius\Component\Customer\Model\CustomerGroup;
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\LiveProp;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;
#[AsLiveComponent]
class FormComponent
{
use ComponentWithFormTrait;
use DefaultActionTrait;
use HookableLiveComponentTrait;
#[LiveProp(fieldName: 'formData')]
public ?CustomerGroup $customerGroup = null;
/** @param class-string $formClass */
public function __construct(
private readonly FormFactoryInterface $formFactory,
private readonly string $formClass,
) {
}
protected function instantiateForm(): FormInterface
{
return $this->formFactory->create($this->formClass, $this->customerGroup);
}
}

View file

@ -13,9 +13,9 @@ declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Twig\Component\Shared\Navbar;
use Sylius\Component\User\Model\UserInterface;
use Sylius\Bundle\AdminBundle\Provider\LoggedInAdminUserProviderInterface;
use Sylius\Component\Core\Model\AdminUserInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
@ -24,35 +24,28 @@ class UserDropdownComponent
public function __construct(
private UrlGeneratorInterface $urlGenerator,
private TranslatorInterface $translator,
private Security $security,
private LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
) {
}
#[ExposeInTemplate(name: 'user')]
public function getUser(): UserInterface
public function getUser(): AdminUserInterface
{
$user = $this->security->getUser();
if (!$user instanceof UserInterface) {
throw new \RuntimeException('User must be an instance of Sylius\Component\User\Model\UserInterface');
$user = $this->loggedInAdminUserProvider->getUser();
if (!$user instanceof AdminUserInterface) {
throw new \RuntimeException('User must be an instance of ' . AdminUserInterface::class . '.');
}
return $user;
}
/**
* @return array<string, array<string, array{title?: string, url?: string, icon?: string, type?: string, class?: string}>>
*
* @psalm-suppress InvalidReturnType
* @return array<array-key, array<array-key, array{title?: string, url?: string, icon?: string, type?: string, class?: string, attr?: array<string, mixed>}>>
*/
#[ExposeInTemplate(name: 'menu_items')]
public function getMenuItems(): array
{
/**
* @phpstan-ignore-next-line PHPStan complains the declared return type does not match the returned value
*
* @psalm-suppress InvalidReturnStatement
*/
// TODO: Would be nice to have these set via hook //
return [
[
'title' => $this->translator->trans('sylius.ui.my_account'),

View file

@ -13,18 +13,17 @@ declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Twig\ErrorTemplateFinder;
use Sylius\Bundle\AdminBundle\Provider\LoggedInAdminUserProviderInterface;
use Sylius\Bundle\AdminBundle\SectionResolver\AdminSection;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
use Sylius\Bundle\UiBundle\Twig\ErrorTemplateFinder\ErrorTemplateFinderInterface;
use Sylius\Component\Core\Model\AdminUserInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Twig\Environment;
final readonly class ErrorTemplateFinder implements ErrorTemplateFinderInterface
{
public function __construct(
private SectionProviderInterface $sectionProvider,
private TokenStorageInterface $tokenStorage,
private LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
private Environment $twig,
) {
}
@ -33,7 +32,7 @@ final readonly class ErrorTemplateFinder implements ErrorTemplateFinderInterface
{
$section = $this->sectionProvider->getSection();
if ($section instanceof AdminSection && $this->isLoggedInAdmin()) {
if ($section instanceof AdminSection && $this->loggedInAdminUserProvider->hasUser()) {
$template = sprintf('@SyliusAdmin/errors/error%s.html.twig', $statusCode);
if ($this->twig->getLoader()->exists($template)) {
return $template;
@ -47,9 +46,4 @@ final readonly class ErrorTemplateFinder implements ErrorTemplateFinderInterface
return null;
}
private function isLoggedInAdmin(): bool
{
return $this->tokenStorage->getToken()?->getUser() instanceof AdminUserInterface;
}
}

View file

@ -0,0 +1,272 @@
<?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 spec\Sylius\Bundle\AdminBundle\Provider;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\AdminBundle\Provider\LoggedInAdminUserProviderInterface;
use Sylius\Component\Core\Model\AdminUserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
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(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
UserRepositoryInterface $adminUserRepository,
) {
$this->beConstructedWith($security, $tokenStorage, $requestStack, $adminUserRepository);
}
function it_implements_logged_in_admin_user_provider(): void
{
$this->shouldImplement(LoggedInAdminUserProviderInterface::class);
}
function it_returns_true_when_user_is_in_security(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn($adminUser);
$tokenStorage->getToken()->shouldNotBeCalled();
$requestStack->getMainRequest()->shouldNotBeCalled();
$requestStack->getSession()->shouldNotBeCalled();
$this->hasUser()->shouldReturn(true);
}
function it_returns_true_when_user_is_in_token_storage(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn(null);
$token->getUser()->willReturn($adminUser);
$tokenStorage->getToken()->willReturn($token);
$requestStack->getMainRequest()->shouldNotBeCalled();
$requestStack->getSession()->shouldNotBeCalled();
$this->hasUser()->shouldReturn(true);
}
function it_returns_true_when_user_is_in_main_request_session_token(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
Request $request,
Session $session,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$token->getUser()->willReturn($adminUser);
$session->get(self::SECURITY_SESSION_KEY)->willReturn('serialized_token');
$request->getSession()->willReturn($session);
$requestStack->getMainRequest()->willReturn($request);
$requestStack->getSession()->shouldNotBeCalled();
$this->hasUser()->shouldReturn(true);
}
function it_returns_true_when_user_is_in_current_request_session_token(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
Session $session,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$token->getUser()->willReturn($adminUser);
$session->get(self::SECURITY_SESSION_KEY)->willReturn('serialized_token');
$requestStack->getMainRequest()->willReturn(null);
$requestStack->getSession()->willReturn($session);
$this->hasUser()->shouldReturn(true);
}
function it_returns_false_when_there_is_no_user(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
Session $session,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$requestStack->getMainRequest()->willReturn(null);
$session->get(self::SECURITY_SESSION_KEY)->willReturn(null);
$requestStack->getSession()->willReturn($session);
$this->hasUser()->shouldReturn(false);
}
function it_returns_false_when_user_cannot_be_provided_and_session_is_not_available_in_current_request(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$requestStack->getMainRequest()->willReturn(null);
$requestStack->getSession()->willThrow(SessionNotFoundException::class);
$this->hasUser()->shouldReturn(false);
}
function it_returns_false_when_user_cannot_be_provided_and_session_is_not_available_in_main_request(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
Request $request,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$requestStack->getMainRequest()->willReturn($request);
$request->getSession()->willThrow(SessionNotFoundException::class);
$this->hasUser()->shouldReturn(false);
}
function it_gets_user_from_security(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
UserRepositoryInterface $adminUserRepository,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn($adminUser);
$tokenStorage->getToken()->shouldNotBeCalled();
$requestStack->getMainRequest()->shouldNotBeCalled();
$requestStack->getSession()->shouldNotBeCalled();
$adminUserRepository->find(Argument::any())->shouldNotBeCalled();
$this->getUser()->shouldReturn($adminUser);
}
function it_gets_user_from_token_storage(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
UserRepositoryInterface $adminUserRepository,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn(null);
$token->getUser()->willReturn($adminUser);
$tokenStorage->getToken()->willReturn($token);
$requestStack->getMainRequest()->shouldNotBeCalled();
$requestStack->getSession()->shouldNotBeCalled();
$adminUserRepository->find(Argument::any())->shouldNotBeCalled();
$this->getUser()->shouldReturn($adminUser);
}
function it_gets_user_from_main_request_session(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
UserRepositoryInterface $adminUserRepository,
Request $request,
Session $session,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$token->getUser()->willReturn($adminUser);
$session->get(self::SECURITY_SESSION_KEY)->willReturn(self::SERIALIZED_TOKEN);
$request->getSession()->willReturn($session);
$requestStack->getMainRequest()->willReturn($request);
$adminUser->getId()->willReturn(404);
$adminUserRepository->find(404)->willReturn($adminUser);
$requestStack->getSession()->shouldNotBeCalled();
$this->getUser()->shouldReturn($adminUser);
}
function it_gets_user_from_current_request_session(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
UserRepositoryInterface $adminUserRepository,
Request $request,
Session $session,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$requestStack->getMainRequest()->willReturn(null);
$token->getUser()->willReturn($adminUser);
$session->get(self::SECURITY_SESSION_KEY)->willReturn(self::SERIALIZED_TOKEN);
$request->getSession()->willReturn($session);
$requestStack->getSession()->willReturn($session);
$adminUser->getId()->willReturn(404);
$adminUserRepository->find(404)->willReturn($adminUser);
$this->getUser()->shouldReturn($adminUser);
}
function it_returns_null_when_user_cannot_be_provided(
Security $security,
TokenStorageInterface $tokenStorage,
RequestStack $requestStack,
UserRepositoryInterface $adminUserRepository,
): void {
$security->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn(null);
$requestStack->getMainRequest()->willReturn(null);
$requestStack->getSession()->willThrow(SessionNotFoundException::class);
$adminUserRepository->find(Argument::any())->shouldNotBeCalled();
$this->getUser()->shouldReturn(null);
}
}

View file

@ -14,17 +14,11 @@ declare(strict_types=1);
namespace spec\Sylius\Bundle\AdminBundle\Twig\ErrorTemplateFinder;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\AdminBundle\Provider\LoggedInAdminUserProviderInterface;
use Sylius\Bundle\AdminBundle\SectionResolver\AdminSection;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionInterface;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
use Sylius\Bundle\UiBundle\Twig\ErrorTemplateFinder\ErrorTemplateFinderInterface;
use Sylius\Component\Core\Model\AdminUserInterface;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Twig\Environment;
use Twig\Loader\LoaderInterface;
@ -32,9 +26,12 @@ final class ErrorTemplateFinderSpec extends ObjectBehavior
{
private const TEMPLATE_PREFIX = '@SyliusAdmin/errors';
function let(SectionProviderInterface $sectionProvider, TokenStorageInterface $tokenStorage, Environment $twig): void
{
$this->beConstructedWith($sectionProvider, $tokenStorage, $twig);
function let(
SectionProviderInterface $sectionProvider,
LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
Environment $twig,
): void {
$this->beConstructedWith($sectionProvider, $loggedInAdminUserProvider, $twig);
}
function it_implements_error_template_finder_interface(): void
@ -44,58 +41,26 @@ final class ErrorTemplateFinderSpec extends ObjectBehavior
function it_does_not_find_template_for_other_sections_than_admin(
SectionProviderInterface $sectionProvider,
TokenStorageInterface $tokenStorage,
LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
Environment $twig,
SectionInterface $section,
): void {
$sectionProvider->getSection()->willReturn($section);
$tokenStorage->getToken()->shouldNotBeCalled();
$loggedInAdminUserProvider->hasUser()->shouldNotBeCalled();
$twig->getLoader()->shouldNotBeCalled();
$this->findTemplate(404)->shouldReturn(null);
}
function it_does_not_find_template_when_there_is_no_token_in_admin_section(
function it_does_not_find_template_when_there_is_no_admin_user(
SectionProviderInterface $sectionProvider,
TokenStorageInterface $tokenStorage,
LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
Environment $twig,
): void {
$sectionProvider->getSection()->willReturn(new AdminSection());
$tokenStorage->getToken()->willReturn(null);
$twig->getLoader()->shouldNotBeCalled();
$this->findTemplate(404)->shouldReturn(null);
}
function it_does_not_find_template_when_there_is_no_token_user_in_admin_section(
SectionProviderInterface $sectionProvider,
TokenStorageInterface $tokenStorage,
Environment $twig,
TokenInterface $token,
): void {
$sectionProvider->getSection()->willReturn(new AdminSection());
$token->getUser()->willReturn(null);
$tokenStorage->getToken()->willReturn($token);
$twig->getLoader()->shouldNotBeCalled();
$this->findTemplate(404)->shouldReturn(null);
}
function it_does_not_find_template_when_the_token_user_is_not_an_admin_in_admin_section(
SectionProviderInterface $sectionProvider,
TokenStorageInterface $tokenStorage,
Environment $twig,
TokenInterface $token,
UserInterface $user,
): void {
$sectionProvider->getSection()->willReturn(new AdminSection());
$token->getUser()->willReturn($user);
$tokenStorage->getToken()->willReturn($token);
$loggedInAdminUserProvider->hasUser()->willReturn(false);
$twig->getLoader()->shouldNotBeCalled();
@ -104,18 +69,14 @@ final class ErrorTemplateFinderSpec extends ObjectBehavior
function it_finds_template_for_admin(
SectionProviderInterface $sectionProvider,
TokenStorageInterface $tokenStorage,
LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
Environment $twig,
LoaderInterface $loader,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$templateName = self::TEMPLATE_PREFIX . '/error404.html.twig';
$sectionProvider->getSection()->willReturn(new AdminSection());
$token->getUser()->willReturn($adminUser);
$tokenStorage->getToken()->willReturn($token);
$loggedInAdminUserProvider->hasUser()->willReturn(true);
$twig->getLoader()->willReturn($loader);
$loader->exists($templateName)->shouldBeCalled()->willReturn(true);
@ -125,19 +86,15 @@ final class ErrorTemplateFinderSpec extends ObjectBehavior
function it_returns_null_if_neither_template_can_be_found(
SectionProviderInterface $sectionProvider,
TokenStorageInterface $tokenStorage,
LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
Environment $twig,
LoaderInterface $loader,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$templateName = self::TEMPLATE_PREFIX . '/error404.html.twig';
$fallbackTemplateName = self::TEMPLATE_PREFIX . '/error.html.twig';
$sectionProvider->getSection()->willReturn(new AdminSection());
$token->getUser()->willReturn($adminUser);
$tokenStorage->getToken()->willReturn($token);
$loggedInAdminUserProvider->hasUser()->willReturn(true);
$twig->getLoader()->willReturn($loader);
$loader->exists($templateName)->shouldBeCalled()->willReturn(false);
@ -148,19 +105,15 @@ final class ErrorTemplateFinderSpec extends ObjectBehavior
function it_finds_fallback_template_for_admin(
SectionProviderInterface $sectionProvider,
TokenStorageInterface $tokenStorage,
LoggedInAdminUserProviderInterface $loggedInAdminUserProvider,
Environment $twig,
LoaderInterface $loader,
TokenInterface $token,
AdminUserInterface $adminUser,
): void {
$templateName = self::TEMPLATE_PREFIX . '/error404.html.twig';
$fallbackTemplateName = self::TEMPLATE_PREFIX . '/error.html.twig';
$sectionProvider->getSection()->willReturn(new AdminSection());
$token->getUser()->willReturn($adminUser);
$tokenStorage->getToken()->willReturn($token);
$loggedInAdminUserProvider->hasUser()->willReturn(true);
$twig->getLoader()->willReturn($loader);
$loader->exists($templateName)->shouldBeCalled()->willReturn(false);

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.code, sylius_test_form_attribute('code')) }}
</div>

View file

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

Some files were not shown because too many files have changed in this diff Show more