mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
[UPMERGE] 1.13 -> 2.0 (#15666)
This PR has been generated automatically. For more details see [upmerge_pr.yaml](/Sylius/Sylius/blob/1.13/.github/workflows/upmerge_pr.yaml). **Remember!** The upmerge should always be merged with using `Merge pull request` button. In case of conflicts, please resolve them manually with usign the following commands: ``` git fetch upstream gh pr checkout git merge upstream/2.0 --no-commit ``` If you use other name for the upstream remote, please replace `upstream` with the name of your remote pointing to the `Sylius/Sylius` repository. Once the conflicts are resolved, please run `git merge --continue` and change the commit title to ``` Resolve conflicts between 1.13 and 2.0 ```
This commit is contained in:
commit
d0b82fd975
223 changed files with 4902 additions and 791 deletions
2
.github/workflows/refactor.yaml
vendored
2
.github/workflows/refactor.yaml
vendored
|
|
@ -3,7 +3,7 @@ name: Refactor
|
|||
on:
|
||||
schedule:
|
||||
-
|
||||
cron: "0 2 * * MON" # Run at 2am every Monday
|
||||
cron: "0 2 * * *" # Run every day at 2am
|
||||
workflow_dispatch: ~
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
@admin_dashboard
|
||||
Feature: Statistics dashboard in a single channel
|
||||
In order to have an overview of my sales
|
||||
As an Administrator
|
||||
I want to see overall statistics on my admin dashboard
|
||||
|
||||
Background:
|
||||
Given the store operates on a single channel in "United States"
|
||||
And the store ships everywhere for Free
|
||||
And the store allows paying Offline
|
||||
And the store has a product "Sylius T-Shirt"
|
||||
And this product has "Red XL" variant priced at "$40.00"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
Scenario: Seeing basic statistics for entire store
|
||||
Given 3 customers have fulfilled 4 orders placed for total of "$8,566.00"
|
||||
And 2 more customers have paid 2 orders placed for total of "$459.00"
|
||||
When I open administration dashboard
|
||||
Then I should see 6 new orders
|
||||
And I should see 5 new customers
|
||||
And there should be total sales of "$9,025.00"
|
||||
And the average order value should be "$1,504.17"
|
||||
|
||||
@ui
|
||||
Scenario: Statistics include only fulfilled orders that were not cancelled
|
||||
Given 4 customers have fulfilled 4 orders placed for total of "$5,241.00"
|
||||
And 2 more customers have placed 2 orders for total of "$459.00"
|
||||
And 2 customers have added products to the cart for total of "$3,450.00"
|
||||
And a single customer has placed an order for total of "$1,000.00"
|
||||
But the customer cancelled this order
|
||||
When I open administration dashboard
|
||||
Then I should see 4 new orders
|
||||
And I should see 9 new customers
|
||||
And there should be total sales of "$5,241.00"
|
||||
And the average order value should be "$1,310.25"
|
||||
|
||||
@ui
|
||||
Scenario: Seeing recent orders and customers
|
||||
Given 2 customers have placed 3 orders for total of "$340.00"
|
||||
And 2 customers have added products to the cart for total of "$424.00"
|
||||
When I open administration dashboard
|
||||
Then I should see 4 new customers in the list
|
||||
And I should see 3 new orders in the list
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
@admin_dashboard
|
||||
Feature: Statistics dashboard per channel
|
||||
In order to have an overview of my sales
|
||||
As an Administrator
|
||||
I want to see overall statistics on my admin dashboard in a specific channel
|
||||
|
||||
Background:
|
||||
Given the store operates on a channel named "Poland"
|
||||
And there is product "Onion" available in this channel
|
||||
And the store operates on another channel named "United States"
|
||||
And there is product "Banana" available in that channel
|
||||
And the store ships everywhere for Free
|
||||
And the store allows paying Offline
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
Scenario: Seeing basic statistics for the first channel by default
|
||||
Given 3 customers have fulfilled 4 orders placed for total of "$8,566.00" mostly "Onion" product
|
||||
And 2 more customers have fulfilled 2 orders placed for total of "$459.00" mostly "Banana" product
|
||||
When I open administration dashboard
|
||||
Then I should see 4 new orders
|
||||
And I should see 5 new customers
|
||||
And there should be total sales of "$8,566.00"
|
||||
And the average order value should be "$2,141.50"
|
||||
|
||||
@ui
|
||||
Scenario: Changing channel in administration dashboard
|
||||
Given 4 customers have fulfilled 4 orders placed for total of "$5,241.00" mostly "Onion" product
|
||||
And 2 more customers have fulfilled 2 orders placed for total of "$459.00" mostly "Banana" product
|
||||
And 2 more customers have placed 3 orders for total of "$1,259.00" mostly "Banana" product
|
||||
When I open administration dashboard
|
||||
And I choose "United States" channel
|
||||
Then I should see 2 new orders
|
||||
And I should see 8 new customers
|
||||
And there should be total sales of "$459.00"
|
||||
And the average order value should be "$229.50"
|
||||
|
||||
@ui
|
||||
Scenario: Seeing recent orders in a specific channel
|
||||
Given 3 customers have placed 4 orders for total of "$8,566.00" mostly "Onion" product
|
||||
And 2 more customers have placed 2 orders for total of "$459.00" mostly "Banana" product
|
||||
When I open administration dashboard for "Poland" channel
|
||||
Then I should see 4 new orders in the list
|
||||
|
||||
@ui
|
||||
Scenario: Seeing recent orders in a specific channel
|
||||
Given 3 customers have placed 4 orders for total of "$8,566.00" mostly "Onion" product
|
||||
And 2 more customers have placed 2 orders for total of "$459.00" mostly "Banana" product
|
||||
When I open administration dashboard for "United States" channel
|
||||
Then I should see 2 new orders in the list
|
||||
67
features/admin/statistics.feature
Normal file
67
features/admin/statistics.feature
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
@admin_dashboard
|
||||
Feature: Statistics
|
||||
In order to gain insight into my sales performance and customers activity
|
||||
As an Administrator
|
||||
I want to view comprehensive statistics
|
||||
|
||||
Background:
|
||||
Given the store operates on a single channel in "United States"
|
||||
And the store ships everywhere for Free
|
||||
And the store allows paying Offline
|
||||
And the store has a product "Sylius T-Shirt"
|
||||
And this product has "Red XL" variant priced at "$40.00"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Seeing statistics for the current year and default channel when expectations are not specified
|
||||
Given it is "2022-12-31" now
|
||||
And 2 new customers have fulfilled 2 orders placed for total of "$1,000.00"
|
||||
And it is "2023-01-01" now
|
||||
And 3 new customers have fulfilled 4 orders placed for total of "$2,000.21"
|
||||
And it is "2023-02-01" now
|
||||
And 2 more new customers have paid 2 orders placed for total of "$5,000.37"
|
||||
When I view statistics
|
||||
Then I should see 5 new customers
|
||||
And I should see 6 new orders
|
||||
And there should be total sales of "$7,000.58"
|
||||
And the average order value should be "$1,166.76"
|
||||
|
||||
@ui @javascript
|
||||
Scenario: Seeing statistics for the previous year
|
||||
Given it is "2022-01-01" now
|
||||
And 3 new customers have fulfilled 2 orders placed for total of "$2,000.00"
|
||||
And it is "2023-02-01" now
|
||||
And 4 more new customers have paid 5 orders placed for total of "$5,000.37"
|
||||
And 2 more new customers have paid 2 orders placed for total of "$5,000.37"
|
||||
When I view statistics for "United States" channel and previous year split by month
|
||||
Then I should see 3 new customers
|
||||
And I should see 2 new orders
|
||||
And there should be total sales of "$2,000.00"
|
||||
And the average order value should be "$1,000.00"
|
||||
|
||||
@ui @javascript
|
||||
Scenario: Seeing statistics for the next year
|
||||
Given it is "2022-01-01" now
|
||||
And 3 new customers have fulfilled 2 orders placed for total of "$2,000.00"
|
||||
And it is "2023-02-01" now
|
||||
And 4 more new customers have paid 5 orders placed for total of "$5,000.37"
|
||||
And 2 more new customers have paid 2 orders placed for total of "$5,000.37"
|
||||
When I view statistics for "United States" channel and previous year split by month
|
||||
And I view statistics for "United States" channel and next year split by month
|
||||
Then I should see 6 new customers
|
||||
And I should see 7 new orders
|
||||
And there should be total sales of "$10,000.74"
|
||||
And the average order value should be "$1,428.68"
|
||||
|
||||
@ui @javascript
|
||||
Scenario: Seeing statistics that include only fulfilled orders that were not cancelled
|
||||
Given 4 new customers have fulfilled 4 orders placed for total of "$5,241.00"
|
||||
And 2 more new customers have placed 2 orders for total of "$459.00"
|
||||
And 2 new customers have added products to the cart for total of "$3,450.00"
|
||||
And a single customer has placed an order for total of "$1,000.00"
|
||||
But the customer cancelled this order
|
||||
When I view statistics for "United States" channel and current year split by month
|
||||
Then I should see 4 new orders
|
||||
And I should see 9 new customers
|
||||
And there should be total sales of "$5,241.00"
|
||||
And the average order value should be "$1,310.25"
|
||||
62
features/admin/statistics_per_channel.feature
Normal file
62
features/admin/statistics_per_channel.feature
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
@admin_dashboard
|
||||
Feature: Statistics for a specific channel
|
||||
In order to gain insight into my sales performance and customers activity in a specific channel
|
||||
As an Administrator
|
||||
I want to view comprehensive statistics
|
||||
|
||||
Background:
|
||||
Given the store operates on a channel named "WEB-POLAND"
|
||||
And there is product "Onion" available in this channel
|
||||
And the store operates on another channel named "WEB-US"
|
||||
And there is product "Banana" available in that channel
|
||||
And the store ships everywhere for Free
|
||||
And the store allows paying Offline
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Seeing basic statistics for the first channel by default
|
||||
Given 3 new customers have fulfilled 4 orders placed for total of "$8,566.00" mostly "Onion" product
|
||||
And 2 more new customers have fulfilled 2 orders placed for total of "$459.00" mostly "Banana" product
|
||||
When I view statistics
|
||||
Then I should see 4 new orders
|
||||
And I should see 5 new customers
|
||||
And there should be total sales of "$8,566.00"
|
||||
And the average order value should be "$2,141.50"
|
||||
|
||||
@ui @javascript
|
||||
Scenario: Switching to the channel with only fulfilled orders
|
||||
Given 4 new customers have fulfilled 4 orders placed for total of "$5,241.00" mostly "Onion" product
|
||||
And 2 more new customers have fulfilled 2 orders placed for total of "$459.00" mostly "Banana" product
|
||||
And 2 more new customers have placed 3 orders for total of "$1,259.00" mostly "Banana" product
|
||||
When I view statistics for "WEB-POLAND" channel and current year split by month
|
||||
And I choose "WEB-US" channel
|
||||
Then I should see 2 new orders
|
||||
And I should see 8 new customers
|
||||
And there should be total sales of "$459.00"
|
||||
And the average order value should be "$229.50"
|
||||
|
||||
@ui @javascript
|
||||
Scenario: Switching to the channel with both fulfilled and placed orders
|
||||
Given 4 new customers have fulfilled 4 orders placed for total of "$5,241.00" mostly "Onion" product
|
||||
And 2 more new customers have fulfilled 2 orders placed for total of "$459.00" mostly "Banana" product
|
||||
And 2 more new customers have placed 3 orders for total of "$1,259.00" mostly "Banana" product
|
||||
When I view statistics for "WEB-US" channel
|
||||
And I choose "WEB-POLAND" channel
|
||||
Then I should see 4 new orders
|
||||
And I should see 8 new customers
|
||||
And there should be total sales of "$5,241.00"
|
||||
And the average order value should be "$1,310.25"
|
||||
|
||||
@ui
|
||||
Scenario: Seeing recent orders in a specific channel
|
||||
Given 3 new customers have placed 4 orders for total of "$8,566.00" mostly "Onion" product
|
||||
And 2 more new customers have placed 2 orders for total of "$459.00" mostly "Banana" product
|
||||
When I view statistics for "WEB-POLAND" channel
|
||||
Then I should see 4 new orders in the list
|
||||
|
||||
@ui
|
||||
Scenario: Seeing recent orders in a specific channel
|
||||
Given 3 new customers have placed 4 orders for total of "$8,566.00" mostly "Onion" product
|
||||
And 2 more new customers have placed 2 orders for total of "$459.00" mostly "Banana" product
|
||||
When I view statistics for "WEB-US" channel
|
||||
Then I should see 2 new orders in the list
|
||||
|
|
@ -172,20 +172,3 @@ Feature: Allowing access only for correctly logged in users
|
|||
And the customer has product "Stark T-Shirt" in the cart
|
||||
And the customer logged out
|
||||
Then the visitor has no access to change product "Stark T-Shirt" quantity to 2 in the customer cart
|
||||
|
||||
@api
|
||||
Scenario: Accessing to the customers cart by the admin
|
||||
Given the customer logged in
|
||||
And the customer has product "Stark T-Shirt" in the cart
|
||||
And the customer logged out
|
||||
And there is logged in the administrator
|
||||
When the administrator try to see the summary of customer's cart
|
||||
Then the administrator should see "Stark T-Shirt" product with quantity 1 in the customer cart
|
||||
|
||||
@api
|
||||
Scenario: Accessing to the visitors cart by the admin
|
||||
Given there is the visitor
|
||||
And the visitor has product "Stark T-Shirt" in the cart
|
||||
And there is logged in the administrator
|
||||
When the administrator try to see the summary of customer's cart
|
||||
Then the administrator should see "Stark T-Shirt" product with quantity 1 in the visitor cart
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Feature: Shipments are in the state "ready" after checkout
|
|||
And the customer chose "Free" shipping method with "Cash on Delivery" payment
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Checking shipment state of a placed order
|
||||
When I view the summary of the order "#00000666"
|
||||
Then it should have shipment in state "Ready"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Feature: Shipping an order
|
|||
And the customer chose "Free" shipping method with "Cash on Delivery" payment
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Finalizing order's shipment
|
||||
Given I view the summary of the order "#00000666"
|
||||
When specify its tracking code as "#00044"
|
||||
|
|
@ -23,13 +23,13 @@ Feature: Shipping an order
|
|||
Then I should be notified that the order has been successfully shipped
|
||||
And it should have shipment in state shipped
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Unable to finalize shipped order's shipment
|
||||
Given this order has already been shipped
|
||||
When I view the summary of the order "#00000666"
|
||||
Then I should not be able to ship this order
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Checking the shipment state of a completed order
|
||||
Given this order has already been shipped
|
||||
When I browse orders
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Feature: Being unable to see details of a cart
|
|||
And the customer added "PHP T-Shirt" product to the cart
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
Scenario: Seeing basic information about an order
|
||||
@ui @api
|
||||
Scenario: Being unable to see the details of a cart
|
||||
When I try to view the summary of the customer's latest cart
|
||||
Then I should be informed that the order does not exist
|
||||
|
|
|
|||
|
|
@ -13,20 +13,22 @@ Feature: Tracking changes on order addresses
|
|||
And the customer bought a single "Italian suit"
|
||||
When I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Browsing order's addresses history after changing it by customer
|
||||
Given the customer "Barney Stinson" addressed it to "East 84st Street and 3rd Avenue", "10118" "New York" in the "United States" with identical billing address
|
||||
And the customer changed shipping address' street to "211 Madison Avenue"
|
||||
And the customer chose "Free" shipping method with "Cash on Delivery" payment
|
||||
When I browse order's "#00000001" history
|
||||
Then there should be 2 changes in the registry
|
||||
Then there should be 2 shipping address changes in the registry
|
||||
Then there should be 1 billing address changes in the registry
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Browsing order's addresses history after changing it by administrator
|
||||
Given the customer "Barney Stinson" addressed it to "East 84st Street and 3rd Avenue", "10118" "New York" in the "United States" with identical billing address
|
||||
And the customer chose "Free" shipping method with "Cash on Delivery" payment
|
||||
When I want to modify a customer's shipping address of this order
|
||||
And I specify their shipping address as "New York", "150 W. 85th Street", "10028", "United States" for "Ted Mosby"
|
||||
When I want to modify a customer's billing address of this order
|
||||
And I specify their billing address as "New York", "150 W. 85th Street", "10028", "United States" for "Ted Mosby"
|
||||
And I save my changes
|
||||
And I browse order's "#00000001" history
|
||||
Then there should be 2 changes in the registry
|
||||
Then there should be 1 shipping address changes in the registry
|
||||
Then there should be 2 billing address changes in the registry
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ Feature: Accessing payment's order from the payment index
|
|||
And there is an "#00000001" order with "Apple" product
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
Scenario: Accessing payment's order from the payments index
|
||||
@ui @api
|
||||
Scenario: Accessing payment's order from the payment
|
||||
Given I am browsing payments
|
||||
When I go to the details of the first payment's order
|
||||
Then I should see order page with details of order "#00000001"
|
||||
Then I should see the details of order "#00000001"
|
||||
|
|
|
|||
|
|
@ -9,10 +9,16 @@ Feature: Adding associations to an existing product
|
|||
And the store has "LG G3", "LG headphones" and "LG earphones" products
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @no-api
|
||||
Scenario: Adding an association to an existing product
|
||||
When I want to modify the "LG G3" product
|
||||
And I associate as "Accessories" the "LG headphones" and "LG earphones" products
|
||||
And I save my changes
|
||||
Then I should be notified that it has been successfully edited
|
||||
And this product should have an association "Accessories" with products "LG headphones" and "LG earphones"
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Adding an association to na existing product
|
||||
When I associate as "Accessories" the product "LG G3" with the products "LG headphones" and "LG earphones"
|
||||
And this product should have an association "Accessories"
|
||||
And this association should have products "LG headphones" and "LG earphones"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ Feature: Adding a new product with associations
|
|||
And this product should not have an association "Accessories" with product "LG earphones"
|
||||
And the product "LG G3" should appear in the store
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @no-api
|
||||
Scenario: Adding a new product with association with numeric code
|
||||
Given the store has 123 product association type
|
||||
When I want to create a new simple product
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Feature: Changing associations of an existing product
|
|||
And the store has "LG G3", "LG headphones" and "LG earphones" products
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @no-api
|
||||
Scenario: Changing associated products of a product association
|
||||
Given the product "LG G3" has an association "Accessories" with product "LG headphones"
|
||||
When I want to modify the "LG G3" product
|
||||
|
|
@ -18,7 +18,19 @@ Feature: Changing associations of an existing product
|
|||
Then I should be notified that it has been successfully edited
|
||||
And this product should have an association "Accessories" with products "LG headphones" and "LG earphones"
|
||||
|
||||
@ui @javascript
|
||||
@api @no-ui
|
||||
Scenario: Adding another product to a product association
|
||||
Given the product "LG G3" has an association "Accessories" with product "LG headphones"
|
||||
When I add the product "LG earphones" to this product association
|
||||
Then this association should have products "LG headphones" and "LG earphones"
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Changing a product of a product association
|
||||
Given the product "LG G3" has an association "Accessories" with product "LG headphones"
|
||||
When I change this product association's product to the "LG earphones" product
|
||||
Then this association should only have product "LG earphones"
|
||||
|
||||
@ui @javascript @no-api
|
||||
Scenario: Removing an associated product of a product association
|
||||
Given the product "LG G3" has an association "Accessories" with products "LG headphones" and "LG earphones"
|
||||
When I want to modify the "LG G3" product
|
||||
|
|
@ -27,3 +39,9 @@ Feature: Changing associations of an existing product
|
|||
Then I should be notified that it has been successfully edited
|
||||
And this product should have an association "Accessories" with product "LG headphones"
|
||||
And this product should not have an association "Accessories" with product "LG earphones"
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Removing a product of a product association
|
||||
Given the product "LG G3" has an association "Accessories" with products "LG headphones" and "LG earphones"
|
||||
When I remove the product "LG earphones" from this product association
|
||||
Then this association should only have product "LG headphones"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Feature: Filtering products by a channel
|
|||
And the store also has a product "HP Spectre" in channel "Web-US"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Filtering products by a chosen channel
|
||||
When I browse products
|
||||
And I choose "Web-EU" as a channel filter
|
||||
|
|
@ -22,7 +22,7 @@ Feature: Filtering products by a channel
|
|||
And I should see a product with name "MacBook Pro"
|
||||
But I should not see any product with name "HP Spectre"
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Filtering products by a chosen channel
|
||||
When I browse products
|
||||
And I choose "Web-US" as a channel filter
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ Feature: Sorting listed products
|
|||
Then I should see 3 products in the list
|
||||
And the first product on the list should have name "Szałowy Mops"
|
||||
|
||||
@ui @no-postgres
|
||||
@ui @api @no-postgres
|
||||
Scenario: Missing translations are sorted as first when sorting by name ascending
|
||||
When I change my locale to "Polish"
|
||||
And I browse products
|
||||
|
|
@ -74,7 +74,7 @@ Feature: Sorting listed products
|
|||
And the first product on the list shouldn't have a name
|
||||
And the last product on the list should have name "Ekstremalny Mops"
|
||||
|
||||
@ui @no-postgres
|
||||
@ui @api @no-postgres
|
||||
Scenario: Missing translation are sorted as last when sorting by name descending
|
||||
When I change my locale to "Polish"
|
||||
And I browse products
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ Feature: Sorting listed products from a taxon by position
|
|||
I want to sort products from a taxon by their positions
|
||||
|
||||
Background:
|
||||
Given the store classifies its products as "Soft Toys"
|
||||
Given the store operates on a single channel in "United States"
|
||||
And the store classifies its products as "Soft Toys"
|
||||
And the store has a product "Old pug" in the "Soft Toys" taxon at 0th position
|
||||
And the store has a product "Young pug" in the "Soft Toys" taxon at 1st position
|
||||
And the store has a product "Small pug" in the "Soft Toys" taxon at 2nd position
|
||||
|
|
@ -28,7 +29,7 @@ Feature: Sorting listed products from a taxon by position
|
|||
And the store has a product "Ultimate Pug" in the "Soft Toys" taxon at 19th position
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Setting two products to position -1 on the non-last page
|
||||
When I am browsing the 1st page of products from "Soft Toys" taxon
|
||||
And I set the position of "Old pug" to "-1"
|
||||
|
|
@ -38,7 +39,7 @@ Feature: Sorting listed products from a taxon by position
|
|||
Then the one before last product on the list should have name "Old pug" with position 18
|
||||
And the last product on the list should have name "Young pug" with position 19
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Setting two products to position -1 on the last page
|
||||
When I am browsing the 2nd page of products from "Soft Toys" taxon
|
||||
And I set the position of "Pug XL" to "-1"
|
||||
|
|
@ -47,7 +48,7 @@ Feature: Sorting listed products from a taxon by position
|
|||
Then the one before last product on the list should have name "Pug XL" with position 18
|
||||
And the last product on the list should have name "Pug XS" with position 19
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Setting two products to the already occupied position on the other page
|
||||
When I am browsing the 1st page of products from "Soft Toys" taxon
|
||||
And I set the position of "Old pug" to "15"
|
||||
|
|
@ -63,7 +64,7 @@ Feature: Sorting listed products from a taxon by position
|
|||
And the 7th product on this page should be named "Pug Master"
|
||||
And this product should be at position 16
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Setting two products to the already occupied position on the same page
|
||||
When I am browsing the 2nd page of products from "Soft Toys" taxon
|
||||
And I set the position of "Puglet" to "15"
|
||||
|
|
@ -79,7 +80,7 @@ Feature: Sorting listed products from a taxon by position
|
|||
And the 7th product on this page should be named "Pug Master"
|
||||
And this product should be at position 16
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Setting two products to the positions overflowing the max available position on the non-last page
|
||||
When I am browsing the 1st page of products from "Soft Toys" taxon
|
||||
And I set the position of "Old pug" to "25"
|
||||
|
|
@ -89,7 +90,7 @@ Feature: Sorting listed products from a taxon by position
|
|||
Then the one before last product on the list should have name "Old pug" with position 18
|
||||
And the last product on the list should have name "Young pug" with position 19
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Setting two products to the positions overflowing the max available position on the last page
|
||||
When I am browsing the 2nd page of products from "Soft Toys" taxon
|
||||
And I set the position of "Puglet" to "25"
|
||||
|
|
@ -99,33 +100,35 @@ Feature: Sorting listed products from a taxon by position
|
|||
Then the one before last product on the list should have name "Puglet" with position 18
|
||||
And the last product on the list should have name "Pug XL" with position 19
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: New product is added as last one
|
||||
Given I added a product "Big pug"
|
||||
And I assigned this product to "Soft Toys" taxon
|
||||
When I am browsing the 3rd page of products from "Soft Toys" taxon
|
||||
Then the last product on the list should have name "Big pug"
|
||||
Then the last product on the list within this taxon should have name "Big pug"
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Product with position 0 is set as the first one
|
||||
When I am browsing products from "Soft Toys" taxon
|
||||
And I set the position of "Young pug" to 0
|
||||
And I save my new configuration
|
||||
Then the first product on the list should have name "Young pug"
|
||||
And I go to the 1st page of products from "Soft Toys" taxon
|
||||
Then the first product on the list within this taxon should have name "Young pug"
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Being unable to use a non-numeric string as a product position
|
||||
Given I am browsing products from "Soft Toys" taxon
|
||||
When I set the position of "Young pug" to "test"
|
||||
And I save my new configuration
|
||||
Then I should be notified that the position "test" is invalid
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Sort products in descending order
|
||||
When I am browsing products from "Soft Toys" taxon
|
||||
And I start sorting products by position
|
||||
Then the first product on the list should have name "Ultimate Pug"
|
||||
@ui
|
||||
And I sort this taxon's products "descending" by "position"
|
||||
Then the first product on the list within this taxon should have name "Ultimate Pug"
|
||||
|
||||
@ui @api
|
||||
Scenario: Products are sorted by position in ascending order by default
|
||||
When I am browsing products from "Soft Toys" taxon
|
||||
Then the first product on the list should have name "Old pug"
|
||||
Then the first product on the list within this taxon should have name "Old pug"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,6 @@ Feature: Accessing the price history from the configurable product show page
|
|||
@ui @no-api
|
||||
Scenario: Being able to access the price history of variant from the configurable product show page
|
||||
Given I am browsing products
|
||||
When I access "Wyborowa Vodka" product page
|
||||
When I access the "Wyborowa Vodka" product
|
||||
And I access the price history of a product variant "Wyborowa Vodka Exquisite" for "United States" channel
|
||||
Then I should see 1 log entries in the catalog price history
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ Feature: Accessing the price history from the simple product show page
|
|||
@ui @no-api
|
||||
Scenario: Being able to access price history from simple product show page
|
||||
Given I am browsing products
|
||||
When I access "Ursus C-355" product page
|
||||
When I access the "Ursus C-355" product
|
||||
And I access the price history of a simple product for "United States" channel
|
||||
Then I should see 2 log entries in the catalog price history
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ Feature: Accessing to product edit page from show page
|
|||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Accessing to product edit page from product show page
|
||||
When I access "Iron shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
And I go to edit page
|
||||
Then I should be on "Iron shield" product edit page
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Accessing to variant edit page from product show page
|
||||
When I access "Iron shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
And I go to edit page of "Iron shield - very big" variant
|
||||
Then I should be on "Iron shield - very big" variant edit page
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@viewing_products
|
||||
@viewing_product_in_admin_panel
|
||||
Feature: Checking products in the shop while viewing them
|
||||
In order to check a product in shop in all channels it is available in
|
||||
As an Administrator
|
||||
|
|
@ -14,18 +14,18 @@ Feature: Checking products in the shop while viewing them
|
|||
@ui @no-api
|
||||
Scenario: Accessing product show page in shop from the product show page where product is available in more than one channel
|
||||
Given this product is available in the "Europe" channel
|
||||
When I access "Bugatti" product page
|
||||
When I access the "Bugatti" product
|
||||
And I show this product in the "Europe" channel
|
||||
Then I should see this product in the "Europe" channel in the shop
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Accessing product show page in shop from the product show page where product is available in one channel
|
||||
When I access "Bugatti" product page
|
||||
When I access the "Bugatti" product
|
||||
And I show this product in this channel
|
||||
Then I should see this product in the "Europe" channel in the shop
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Being unable to access product show page in shop when the product is disabled
|
||||
Given this product has been disabled
|
||||
When I access "Bugatti" product page
|
||||
When I access the "Bugatti" product
|
||||
Then I should not be able to show this product in shop
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@viewing_products
|
||||
@viewing_product_in_admin_panel
|
||||
Feature: Seeing applied catalog promotions details for a simple product
|
||||
In order to be aware of simple product price change reason
|
||||
As an Administrator
|
||||
|
|
@ -13,8 +13,9 @@ Feature: Seeing applied catalog promotions details for a simple product
|
|||
And it reduces price by "90%"
|
||||
And it is enabled
|
||||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Seeing applied catalog promotion details on a simple product
|
||||
When I access "Ursus C-355" product
|
||||
Then this product price should be decreased by catalog promotion "Company bankruptcy sale" in "United States" channel
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@viewing_products
|
||||
@viewing_product_in_admin_panel
|
||||
Feature: Seeing applied catalog promotions details within variant
|
||||
In order to be aware of variant's price change reason
|
||||
As an Administrator
|
||||
|
|
@ -20,11 +20,20 @@ Feature: Seeing applied catalog promotions details within variant
|
|||
And it reduces price by "37%"
|
||||
And it is enabled
|
||||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Seeing applied catalog promotion details within variant
|
||||
When I access "Wyborowa Vodka" product
|
||||
Then "Wyborowa Vodka Exquisite" variant price should be decreased by catalog promotion "Winter sale" in "United States" channel
|
||||
And "Wyborowa Vodka Lemon" variant price should not be decreased by catalog promotion "Winter sale" in "United States" channel
|
||||
And "Wyborowa Vodka Exquisite" variant price should not be decreased by catalog promotion "Christmas sale" in "United States" channel
|
||||
And "Wyborowa Vodka Lemon" variant price should be decreased by catalog promotion "Christmas sale" in "United States" channel
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Seeing applied catalog promotion details within variant
|
||||
When I view all variants of the product "Wyborowa Vodka"
|
||||
Then "Wyborowa Vodka Exquisite" variant price should be decreased by catalog promotion "Winter sale" in "United States" channel
|
||||
And "Wyborowa Vodka Lemon" variant price should not be decreased by catalog promotion "Winter sale" in "United States" channel
|
||||
And "Wyborowa Vodka Exquisite" variant price should not be decreased by catalog promotion "Christmas sale" in "United States" channel
|
||||
And "Wyborowa Vodka Lemon" variant price should be decreased by catalog promotion "Christmas sale" in "United States" channel
|
||||
|
|
|
|||
|
|
@ -17,55 +17,69 @@ Feature: Viewing details of a product with variants
|
|||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
Scenario: Viewing a configurable product show page
|
||||
When I access "Iron Shield" product page
|
||||
@ui @no-api
|
||||
Scenario: Viewing a configurable product
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product show page with variants
|
||||
And I should see product name "Iron Shield"
|
||||
|
||||
@ui
|
||||
Scenario: Viewing taxonomy block
|
||||
@ui @api
|
||||
Scenario: Viewing taxonomies
|
||||
Given the store classifies its products as "Shield" and "Equipment"
|
||||
And the product "Iron Shield" has a main taxon "Equipment"
|
||||
And the product "Iron Shield" belongs to taxon "Shield"
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see main taxon is "Equipment"
|
||||
And I should see product taxon is "Shield"
|
||||
And I should see product taxon "Shield"
|
||||
|
||||
@ui
|
||||
Scenario: Viewing options block
|
||||
When I access "Iron Shield" product page
|
||||
@ui @api
|
||||
Scenario: Viewing options
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see option "Shield size"
|
||||
|
||||
@ui
|
||||
Scenario: Viewing variants block
|
||||
When I access "Iron Shield" product page
|
||||
@ui @api
|
||||
Scenario: Viewing variants
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see 2 variants
|
||||
And I should see the "Iron Shield - very big" variant
|
||||
And I should see the "Iron Shield - very small" variant
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Viewing variants' details
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see 2 variants
|
||||
And I should see "Iron Shield - very big" variant with code "123456789-xl", priced "$25.00" and current stock 5 and in "United States" channel
|
||||
And I should see "Iron Shield - very small" variant with code "123456789-xs", priced "$15.00" and current stock 12 and in "United States" channel
|
||||
|
||||
@ui @javascript @api
|
||||
Scenario: Viewing media block
|
||||
Scenario: Viewing media
|
||||
Given the "Iron Shield" product has an image "mugs.jpg" with "main" type
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see an image related to this product
|
||||
|
||||
@ui
|
||||
Scenario: Viewing "more details" block
|
||||
@ui @api
|
||||
Scenario: Viewing more details
|
||||
Given the product "Iron Shield" has the slug "iron-shield"
|
||||
And the description of product "Iron Shield" is "Shield created by dwarf"
|
||||
And the meta keywords of product "Iron Shield" is "shield"
|
||||
And the short description of product "Iron Shield" is "good shield"
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product name is "Iron Shield"
|
||||
And I should see product slug is "iron-shield"
|
||||
And I should see product's description is "Shield created by dwarf"
|
||||
And I should see product's meta keywords is "shield"
|
||||
And I should see product's short description is "good shield"
|
||||
|
||||
@ui
|
||||
Scenario: Viewing associations block
|
||||
@ui @api
|
||||
Scenario: Viewing association types
|
||||
Given the store has a "Glass shield" product
|
||||
And the product "Iron Shield" has an association "Similar" with product "Glass shield"
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product association type "Similar"
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Viewing associations
|
||||
Given the store has a "Glass shield" product
|
||||
And the product "Iron Shield" has an association "Similar" with product "Glass shield"
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product association "Similar" with "Glass shield"
|
||||
|
|
|
|||
|
|
@ -11,54 +11,54 @@ Feature: Viewing details of a simple product
|
|||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing a simple product show page
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product show page without variants
|
||||
And I should see product name "Iron Shield"
|
||||
And I should see product breadcrumb "Iron Shield"
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing pricing block
|
||||
Given the product "Iron Shield" has original price "$25.00"
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see price "$20.00" for channel "United States"
|
||||
And I should see original price "$25.00" for channel "United States"
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing price block without channel enable
|
||||
Given this product is unavailable in "United States" channel
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product name "Iron Shield"
|
||||
And I should see the product in neither channel
|
||||
And I should not see price for channel "United States"
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing details block
|
||||
Given the store has a tax category "No tax" with a code "nt"
|
||||
And product's "Iron Shield" code is "123456789"
|
||||
And there are 4 units of product "Iron Shield" available in the inventory
|
||||
And the product "Iron Shield" belongs to "No tax" tax category
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product's code is "123456789"
|
||||
And I should see the product is enabled for channel "United States"
|
||||
And I should see 4 as a current stock of this product
|
||||
And I should see product's tax category is "No tax"
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing taxonomy block
|
||||
Given this product belongs to "Shield"
|
||||
And the product "Iron Shield" has a main taxon "Equipment"
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see main taxon is "Equipment"
|
||||
And I should see product taxon is "Shield"
|
||||
And I should see product taxon "Shield"
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing shipping block
|
||||
Given the store has "Over sized" and "Standard" shipping category
|
||||
And the product "Iron Shield" has height "10.0", width "15.0", depth "20.0", weight "25.0"
|
||||
And this product belongs to "Over sized" shipping category
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product's shipping category is "Over sized"
|
||||
And I should see product's height is 10
|
||||
And I should see product's width is 15
|
||||
|
|
@ -68,26 +68,26 @@ Feature: Viewing details of a simple product
|
|||
@ui @javascript @api
|
||||
Scenario: Viewing media block
|
||||
Given the "Iron Shield" product has an image "mugs.jpg" with "main" type
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see an image related to this product
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing "more details" block
|
||||
Given the product "Iron Shield" has the slug "iron-shield"
|
||||
And the description of product "Iron Shield" is "Shield created by dwarf"
|
||||
And the meta keywords of product "Iron Shield" is "shield"
|
||||
And the short description of product "Iron Shield" is "good shield"
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product name is "Iron Shield"
|
||||
And I should see product slug is "iron-shield"
|
||||
And I should see product's description is "Shield created by dwarf"
|
||||
And I should see product's meta keywords is "shield"
|
||||
And I should see product's short description is "good shield"
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Viewing associations block
|
||||
Given the store has "Similar" and "Dwarf equipment" product association types
|
||||
And the store has a "Glass Shield" product
|
||||
And the product "Iron Shield" has an association "Similar" with product "Glass Shield"
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see product association "Similar" with "Glass Shield"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@viewing_products
|
||||
@viewing_product_in_admin_panel
|
||||
Feature: Seeing the lowest price before the discount for a simple product
|
||||
In order to be aware of simple product prices
|
||||
As an Administrator
|
||||
|
|
@ -13,10 +13,10 @@ Feature: Seeing the lowest price before the discount for a simple product
|
|||
@ui @no-api
|
||||
Scenario: Seeing price block with lowest price before the discount
|
||||
Given this product's price changed to "$21.00" and original price changed to "$37.00"
|
||||
When I access "Bizon Z056" product page
|
||||
When I access the "Bizon Z056" product
|
||||
Then I should see "$42.00" as its lowest price before the discount in "United States" channel
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Seeing price block without lowest price before the discount
|
||||
When I access "Bizon Z056" product page
|
||||
When I access the "Bizon Z056" product
|
||||
Then I should not see the lowest price before the discount in "United States" channel
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@viewing_products
|
||||
@viewing_product_in_admin_panel
|
||||
Feature: Seeing the lowest price before the discount within variant
|
||||
In order to be aware of variant's prices
|
||||
As an Administrator
|
||||
|
|
@ -15,6 +15,6 @@ Feature: Seeing the lowest price before the discount within variant
|
|||
|
||||
@ui @no-api
|
||||
Scenario: Seeing price block with lowest price before the discount within variant
|
||||
When I access "Wyborowa Vodka" product page
|
||||
When I access the "Wyborowa Vodka" product
|
||||
Then I should not see the lowest price before the discount for "Wyborowa Vodka Rye" variant in "United States" channel
|
||||
And I should see the lowest price before the discount of "$11.00" for "Wyborowa Vodka Potato" variant in "United States" channel
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@viewing_products
|
||||
@viewing_product_in_admin_panel
|
||||
Feature: Viewing product's non translatable attributes
|
||||
In order to see product's non translatable attribute
|
||||
As an Administrator
|
||||
|
|
@ -14,9 +14,9 @@ Feature: Viewing product's non translatable attributes
|
|||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Viewing product's non translatable attributes along with default ones
|
||||
When I access "Iron Pickaxe" product page
|
||||
Then I should see non-translatable attribute "crit chance" with value "10 %"
|
||||
When I access the "Iron Pickaxe" product
|
||||
Then I should see non-translatable attribute "crit chance" with value 10%
|
||||
And I should see attribute "Material" with value "Iron" in "English (United States)" locale
|
||||
And I should see attribute "Material" with value "Żelazo" in "Polish (Poland)" locale
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@viewing_products
|
||||
@viewing_product_in_admin_panel
|
||||
Feature: Viewing product's attributes in different locales
|
||||
In order to see product's specification in all locales
|
||||
As a Administrator
|
||||
|
|
@ -15,9 +15,9 @@ Feature: Viewing product's attributes in different locales
|
|||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Viewing product's attributes defined in different locales
|
||||
When I access "Iron Shield" product page
|
||||
When I access the "Iron Shield" product
|
||||
Then I should see attribute "material" with value "oak wood" in "English (United States)" locale
|
||||
And I should see attribute "shield details" with value "oak wood is a very good material." in "English (United States)" locale
|
||||
And I should see attribute "material" with value "drewno dębowe" in "Polish (Poland)" locale
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ Feature: Not seeing the lowest price for a product that has a taxon excluded on
|
|||
And the store also has a product "Cauliflower" priced at "$25.00"
|
||||
And it belongs to "Vegetables" and "Special offers"
|
||||
And this product's price changed to "$15.00" and original price changed to "$25.00"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@api @ui
|
||||
Scenario: Not seeing the lowest price for a product that has a taxon excluded on the channel
|
||||
|
|
|
|||
|
|
@ -17,24 +17,24 @@ Feature: Viewing product's select attributes
|
|||
Then I should see the product attribute "T-Shirt material" with value "Banana skin" on the list
|
||||
And I should also see the product attribute "T-Shirt material" with value "Cotton" on the list
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Viewing a detailed page with product's select attribute after changing a value
|
||||
Given this product has select attribute "T-Shirt material" with values "Banana skin" and "Cotton"
|
||||
When the administrator changes this product attribute's value "Cotton" to "Orange skin"
|
||||
When this product attribute's value changed from "Cotton" to "Orange skin"
|
||||
And I check this product's details
|
||||
Then I should see the product attribute "T-Shirt material" with value "Banana skin" on the list
|
||||
And I should also see the product attribute "T-Shirt material" with value "Orange skin" on the list
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api @no-postgres
|
||||
Scenario: Viewing a detailed page with product's select attribute after removing an only value
|
||||
Given this product has select attribute "T-Shirt material" with value "Cotton"
|
||||
When the administrator deletes the value "Cotton" from this product attribute
|
||||
When this product attribute's value "Cotton" has been removed
|
||||
And I check this product's details
|
||||
Then I should not see the product attribute "T-Shirt material"
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api @no-postgres
|
||||
Scenario: Viewing a detailed page with product's select attribute after removing one of the value
|
||||
Given this product has select attribute "T-Shirt material" with values "Banana skin" and "Cotton"
|
||||
When the administrator deletes the value "Cotton" from this product attribute
|
||||
When this product attribute's value "Cotton" has been removed
|
||||
And I check this product's details
|
||||
Then I should see the product attribute "T-Shirt material" with value "Banana skin"
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ Feature: Accessing shipment's order from the shipments index
|
|||
And the customer chose "UPS" shipping method with "Cash on Delivery" payment
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
Scenario: Accessing shipment's order from the shipments index
|
||||
@ui @api
|
||||
Scenario: Accessing shipment's order from the shipment
|
||||
When I browse shipments
|
||||
And I move to the details of first shipment's order
|
||||
Then I should see order page with details of order "00000001"
|
||||
Then I should see the details of order "#00000001"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Feature: Adding a new shipping method with rule
|
|||
And the store has a zone "United States" with code "US"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Adding a new shipping method with total weight greater than or equal rule
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
@ -24,7 +24,7 @@ Feature: Adding a new shipping method with rule
|
|||
Then I should be notified that it has been successfully created
|
||||
And the shipping method "FedEx Carrier" should appear in the registry
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Adding a new shipping method with total weight less than or equal rule
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
@ -38,7 +38,7 @@ Feature: Adding a new shipping method with rule
|
|||
Then I should be notified that it has been successfully created
|
||||
And the shipping method "FedEx Carrier" should appear in the registry
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Adding a new shipping method with order total greater than or equal rule
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
@ -52,7 +52,7 @@ Feature: Adding a new shipping method with rule
|
|||
Then I should be notified that it has been successfully created
|
||||
And the shipping method "FedEx Carrier" should appear in the registry
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Adding a new shipping method with order total less than or equal rule
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
|
|||
|
|
@ -10,14 +10,6 @@ Feature: Editing shipping method
|
|||
And the store allows shipping with "UPS Carrier" identified by "UPS_CARRIER"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@todo
|
||||
Scenario: Trying to change shipping method code
|
||||
When I want to modify a shipping method "UPS Carrier"
|
||||
And I change its code to "UPS"
|
||||
And I save my changes
|
||||
Then I should be notified that code cannot be changed
|
||||
And shipping method "UPS Carrier" should still have code "UPS_CARRIER"
|
||||
|
||||
@ui @api
|
||||
Scenario: Seeing disabled code field when editing shipping method
|
||||
When I want to modify a shipping method "UPS Carrier"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Feature: Shipping method code validation
|
|||
And the store has a zone "United States" with code "US"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Trying to add a new shipping method with special symbols in the code
|
||||
When I want to create a new shipping method
|
||||
And I name it "FedEx Carrier" in "English (United States)"
|
||||
|
|
@ -19,7 +19,7 @@ Feature: Shipping method code validation
|
|||
Then I should be notified that code needs to contain only specific symbols
|
||||
And shipping method with name "FedEx Carrier" should not be added
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Trying to add a new shipping method with spaces in the code
|
||||
When I want to create a new shipping method
|
||||
And I name it "FedEx Carrier" in "English (United States)"
|
||||
|
|
@ -27,14 +27,3 @@ Feature: Shipping method code validation
|
|||
And I try to add it
|
||||
Then I should be notified that code needs to contain only specific symbols
|
||||
And shipping method with name "FedEx Carrier" should not be added
|
||||
|
||||
@ui @javascript
|
||||
Scenario: Trying to add a new shipping method with
|
||||
When I want to create a new shipping method
|
||||
And I name it "FedEx Carrier First US Division" in "English (United States)"
|
||||
And I specify its code as "PEC-US_01"
|
||||
And I define it for the zone named "United States"
|
||||
And I choose "Flat rate per shipment" calculator
|
||||
And I specify its amount as 50 for "Web-US" channel
|
||||
And I add it
|
||||
Then the shipping method "FedEx Carrier First US Division" should appear in the registry
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Feature: Shipping method flat rate per shipment calculator validation
|
|||
And the store has a zone "United States" with code "US"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Trying to add a new shipping method with flat rate per shipment calculator without specifying its amount
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
@ -21,7 +21,7 @@ Feature: Shipping method flat rate per shipment calculator validation
|
|||
Then I should be notified that amount for "Web" channel should not be blank
|
||||
And shipping method with name "FedEx Carrier" should not be added
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Trying to add a new shipping method with flat rate per shipment calculator with charge below 0
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Feature: Shipping method flat rate per unit calculator validation
|
|||
And the store has a zone "United States" with code "US"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Trying to add a new shipping method with flat rate per unit calculator without specifying its amount
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
@ -21,7 +21,7 @@ Feature: Shipping method flat rate per unit calculator validation
|
|||
Then I should be notified that amount for "Web" channel should not be blank
|
||||
And shipping method with name "FedEx Carrier" should not be added
|
||||
|
||||
@ui @javascript
|
||||
@ui @javascript @api
|
||||
Scenario: Trying to add a new shipping method with flat rate per unit calculator with charge below 0
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ Feature: Shipping method validation
|
|||
I want to be prevented from adding it without specifying required fields
|
||||
|
||||
Background:
|
||||
Given the store operates on a single channel in "United States"
|
||||
Given the store operates on a channel named "Web-US" in "USD" currency
|
||||
And the store has a zone "United States" with code "US"
|
||||
And the store is available in "English (United States)"
|
||||
And I am logged in as an administrator
|
||||
|
||||
|
|
@ -46,10 +47,38 @@ Feature: Shipping method validation
|
|||
Then I should be notified that name is required
|
||||
And this shipping method should still be named "UPS Ground"
|
||||
|
||||
@ui
|
||||
@ui @api
|
||||
Scenario: Trying to remove zone from existing shipping method
|
||||
Given the store allows shipping with "UPS Ground"
|
||||
When I want to modify this shipping method
|
||||
And I remove its zone
|
||||
And I try to save my changes
|
||||
Then I should be notified that zone has to be selected
|
||||
Then I should be notified that the zone is required
|
||||
|
||||
@ui @javascript @api
|
||||
Scenario: Adding a new shipping method with order total greater than or equal rule that contains invalid data
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
And I specify its position as 0
|
||||
And I name it "FedEx Carrier" in "English (United States)"
|
||||
And I define it for the zone named "United States"
|
||||
And I choose "Flat rate per shipment" calculator
|
||||
And I specify its amount as 50 for "Web-US" channel
|
||||
And I add the "Total weight greater than or equal" rule configured with invalid data
|
||||
And I add it
|
||||
Then I should be notified that the weight rule has an invalid configuration
|
||||
And the shipping method "FedEx Carrier" should not appear in the registry
|
||||
|
||||
@ui @javascript @api
|
||||
Scenario: Adding a new shipping method with order total less than or equal rule that contains invalid data
|
||||
When I want to create a new shipping method
|
||||
And I specify its code as "FED_EX_CARRIER"
|
||||
And I specify its position as 0
|
||||
And I name it "FedEx Carrier" in "English (United States)"
|
||||
And I define it for the zone named "United States"
|
||||
And I choose "Flat rate per shipment" calculator
|
||||
And I specify its amount as 50 for "Web-US" channel
|
||||
And I add the "Items total less than or equal" rule configured with invalid data for "Web-US" channel
|
||||
And I add it
|
||||
Then I should be notified that the amount rule has an invalid configuration in "Web-US" channel
|
||||
And the shipping method "FedEx Carrier" should not appear in the registry
|
||||
|
|
|
|||
|
|
@ -1581,14 +1581,14 @@ parameters:
|
|||
path: src/Sylius/Bundle/ApiBundle/EventSubscriber/TaxonDeletionEventSubscriber.php
|
||||
|
||||
-
|
||||
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\Filter\\\\Doctrine\\\\CatalogPromotionChannelFilter\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#"
|
||||
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\Filter\\\\Doctrine\\\\ChannelsAwareChannelFilter\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: src/Sylius/Bundle/ApiBundle/Filter/Doctrine/CatalogPromotionChannelFilter.php
|
||||
path: src/Sylius/Bundle/ApiBundle/Filter/Doctrine/ChannelsAwareChannelFilter.php
|
||||
|
||||
-
|
||||
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\Filter\\\\Doctrine\\\\CatalogPromotionChannelFilter\\:\\:getDescription\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\Filter\\\\Doctrine\\\\ChannelsAwareChannelFilter\\:\\:getDescription\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: src/Sylius/Bundle/ApiBundle/Filter/Doctrine/CatalogPromotionChannelFilter.php
|
||||
path: src/Sylius/Bundle/ApiBundle/Filter/Doctrine/ChannelsAwareChannelFilter.php
|
||||
|
||||
-
|
||||
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\Filter\\\\Doctrine\\\\ExchangeRateFilter\\:\\:filterProperty\\(\\) has no return type specified\\.$#"
|
||||
|
|
|
|||
|
|
@ -132,18 +132,50 @@ final class ResponseChecker implements ResponseCheckerInterface
|
|||
return false;
|
||||
}
|
||||
|
||||
/** @param string|int $value */
|
||||
public function hasSubResourceWithValue(Response $response, string $subResource, string $key, $value): bool
|
||||
{
|
||||
foreach ($this->getResponseContentValue($response, $subResource) as $resource) {
|
||||
if ($resource[$key] === $value) {
|
||||
return true;
|
||||
public function hasValuesInAnySubresourceObjectCollection(
|
||||
Response $response,
|
||||
string $subResource,
|
||||
array $expectedValues,
|
||||
): bool {
|
||||
$resourceCollection = $this->getResponseContentValue($response, $subResource);
|
||||
|
||||
$this->assertIsArray($resourceCollection);
|
||||
|
||||
foreach ($resourceCollection as $resource) {
|
||||
$this->assertIsArray($resource);
|
||||
|
||||
foreach ($expectedValues as $key => $expectedValue) {
|
||||
if (!array_key_exists($key, $resource) || $resource[$key] !== $expectedValue) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasValuesInSubresourceObject(
|
||||
Response $response,
|
||||
string $subResource,
|
||||
array $expectedValues,
|
||||
): bool {
|
||||
$resource = $this->getResponseContentValue($response, $subResource);
|
||||
|
||||
$this->assertIsArray($resource);
|
||||
|
||||
$this->assertAllExpectedKeysArePresent($expectedValues, $resource);
|
||||
|
||||
foreach ($expectedValues as $key => $expectedValue) {
|
||||
if ($resource[$key] !== $expectedValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @param string|array $value */
|
||||
public function hasItemOnPositionWithValue(Response $response, int $position, string $key, $value): bool
|
||||
{
|
||||
|
|
@ -234,9 +266,10 @@ final class ResponseChecker implements ResponseCheckerInterface
|
|||
Assert::keyExists(
|
||||
$content,
|
||||
$key,
|
||||
SprintfResponseEscaper::provideMessageWithEscapedResponseContent(
|
||||
'Expected \'' . $key . '\' not found.',
|
||||
$response,
|
||||
sprintf(
|
||||
'Expected to get: "%s" key in response, got keys: [%s]',
|
||||
$key,
|
||||
implode(', ', array_keys($content)),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
@ -253,4 +286,26 @@ final class ResponseChecker implements ResponseCheckerInterface
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function assertIsArray(mixed $resource): void
|
||||
{
|
||||
Assert::isArray($resource, sprintf('Expected to get an array, got "%s"', gettype($resource)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int|string> $expectedValues
|
||||
* @param array<string, int|string> $resource
|
||||
*/
|
||||
private function assertAllExpectedKeysArePresent(array $expectedValues, array $resource): void
|
||||
{
|
||||
Assert::count(
|
||||
array_diff_key($expectedValues, $resource),
|
||||
0,
|
||||
sprintf(
|
||||
'Expected values array has keys: [%s], that are not present in the responses keys: [%s]',
|
||||
implode(', ', array_keys(array_diff_key($expectedValues, $resource))),
|
||||
implode(', ', array_keys($resource)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,19 @@ interface ResponseCheckerInterface
|
|||
|
||||
public function hasItemWithValue(Response $response, string $key, int|string $value): bool;
|
||||
|
||||
public function hasSubResourceWithValue(Response $response, string $subResource, string $key, int|string $value): bool;
|
||||
/** @param array<string, int|string> $expectedValues */
|
||||
public function hasValuesInAnySubresourceObjectCollection(
|
||||
Response $response,
|
||||
string $subResource,
|
||||
array $expectedValues,
|
||||
): bool;
|
||||
|
||||
/** @param array<string, int|string> $expectedValues */
|
||||
public function hasValuesInSubresourceObject(
|
||||
Response $response,
|
||||
string $subResource,
|
||||
array $expectedValues,
|
||||
): bool;
|
||||
|
||||
public function hasItemOnPositionWithValue(Response $response, int $position, string $key, array|string $value): bool;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ use Sylius\Behat\Client\ApiClientInterface;
|
|||
use Sylius\Behat\Client\ResponseCheckerInterface;
|
||||
use Sylius\Behat\Context\Api\Resources;
|
||||
use Sylius\Component\Core\Model\CatalogPromotionInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductVariantInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class BrowsingCatalogPromotionProductVariantsContext implements Context
|
||||
|
|
@ -40,6 +43,17 @@ final class BrowsingCatalogPromotionProductVariantsContext implements Context
|
|||
$this->client->filter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I want to view all variants of (this product)$/
|
||||
* @When /^I view(?:| all) variants of the (product "[^"]+")$/
|
||||
*/
|
||||
public function iWantToViewAllVariantsOfThisProduct(ProductInterface $product): void
|
||||
{
|
||||
$this->client->index(Resources::PRODUCT_VARIANTS);
|
||||
$this->client->addFilter('product', $this->iriConverter->getIriFromResource($product));
|
||||
$this->client->filter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^there should be (\d+) product variants? on the list$/
|
||||
*/
|
||||
|
|
@ -66,4 +80,71 @@ final class BrowsingCatalogPromotionProductVariantsContext implements Context
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then :variant variant price should be decreased by catalog promotion :catalogPromotion in :channel channel
|
||||
*/
|
||||
public function variantPriceShouldBeDecreasedByCatalogPromotion(
|
||||
ProductVariantInterface $variant,
|
||||
CatalogPromotionInterface $catalogPromotion,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
Assert::true(
|
||||
$this->variantHasCatalogPromotionInChannel($variant, $catalogPromotion, $channel),
|
||||
sprintf(
|
||||
'Catalog promotion "%s" was not found in applied promotions of variant "%s" in channel "%s".',
|
||||
$catalogPromotion->getCode(),
|
||||
$variant->getCode(),
|
||||
$channel->getCode(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then :variant variant price should not be decreased by catalog promotion :catalogPromotion in :channel channel
|
||||
*/
|
||||
public function variantPriceShouldNotBeDecreasedByCatalogPromotion(
|
||||
ProductVariantInterface $variant,
|
||||
CatalogPromotionInterface $catalogPromotion,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
Assert::false(
|
||||
$this->variantHasCatalogPromotionInChannel($variant, $catalogPromotion, $channel),
|
||||
sprintf(
|
||||
'Catalog promotion "%s" was found in applied promotions of variant "%s" in channel "%s".',
|
||||
$catalogPromotion->getCode(),
|
||||
$variant->getCode(),
|
||||
$channel->getCode(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function variantHasCatalogPromotionInChannel(
|
||||
ProductVariantInterface $variant,
|
||||
CatalogPromotionInterface $catalogPromotion,
|
||||
ChannelInterface $channel
|
||||
): bool {
|
||||
$variantData = $this->getDataOfVariantWithCode($variant->getCode());
|
||||
|
||||
$promotions = $variantData['channelPricings'][$channel->getCode()]['appliedPromotions'] ?? [];
|
||||
foreach ($promotions as $promotion) {
|
||||
if ($promotion['code'] === $catalogPromotion->getCode()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getDataOfVariantWithCode(string $code): array
|
||||
{
|
||||
$variantsData = $this->responseChecker->getCollection($this->client->getLastResponse());
|
||||
foreach ($variantsData as $variantData) {
|
||||
if ($variantData['code'] === $code) {
|
||||
return $variantData;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Variant with code "%s" was not found.', $code));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@ namespace Sylius\Behat\Context\Api\Admin;
|
|||
use ApiPlatform\Api\IriConverterInterface;
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Behat\Client\ApiClientInterface;
|
||||
use Sylius\Behat\Client\RequestFactoryInterface;
|
||||
use Sylius\Behat\Client\ResponseCheckerInterface;
|
||||
use Sylius\Behat\Context\Api\Resources;
|
||||
use Sylius\Behat\Context\Api\Subresources;
|
||||
use Sylius\Behat\Service\Converter\SectionAwareIriConverterInterface;
|
||||
use Sylius\Behat\Service\SecurityServiceInterface;
|
||||
use Sylius\Behat\Service\SharedSecurityServiceInterface;
|
||||
use Sylius\Behat\Service\SharedStorageInterface;
|
||||
|
|
@ -27,6 +30,7 @@ use Sylius\Component\Core\Model\ChannelInterface;
|
|||
use Sylius\Component\Core\Model\CustomerInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Model\PaymentMethodInterface;
|
||||
use Sylius\Component\Core\Model\ShipmentInterface;
|
||||
use Sylius\Component\Core\Model\ShippingMethodInterface;
|
||||
use Sylius\Component\Currency\Model\CurrencyInterface;
|
||||
use Sylius\Component\Order\OrderTransitions;
|
||||
|
|
@ -41,10 +45,12 @@ final class ManagingOrdersContext implements Context
|
|||
public function __construct(
|
||||
private ApiClientInterface $client,
|
||||
private ResponseCheckerInterface $responseChecker,
|
||||
private RequestFactoryInterface $requestFactory,
|
||||
private IriConverterInterface $iriConverter,
|
||||
private SecurityServiceInterface $adminSecurityService,
|
||||
private SharedStorageInterface $sharedStorage,
|
||||
private SharedSecurityServiceInterface $sharedSecurityService,
|
||||
private SectionAwareIriConverterInterface $sectionAwareIriConverter,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +61,7 @@ final class ManagingOrdersContext implements Context
|
|||
public function iSeeTheOrder(OrderInterface $order): void
|
||||
{
|
||||
$response = $this->client->show(Resources::ORDERS, $order->getTokenValue());
|
||||
Assert::same($this->responseChecker->getValue($response, '@id'), $this->iriConverter->getIriFromResource($order));
|
||||
Assert::same($this->responseChecker->getValue($response, '@id'), $this->sectionAwareIriConverter->getIriFromResourceInSection($order, 'admin'));
|
||||
|
||||
$this->sharedStorage->set('order', $order);
|
||||
}
|
||||
|
|
@ -69,6 +75,14 @@ final class ManagingOrdersContext implements Context
|
|||
$this->client->index(Resources::ORDERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I browse order's :order history
|
||||
*/
|
||||
public function iBrowseOrderHistory(OrderInterface $order): void
|
||||
{
|
||||
$this->iSeeTheOrder($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I filter
|
||||
*/
|
||||
|
|
@ -93,6 +107,30 @@ final class ManagingOrdersContext implements Context
|
|||
$this->client->addFilter('checkoutCompletedAt[after]', $dateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When specify its tracking code as :trackingCode
|
||||
*/
|
||||
public function specifyItsTrackingCodeAs(string $trackingCode): void
|
||||
{
|
||||
$shipment = $this->sharedStorage->get('order')->getShipments()->first();
|
||||
|
||||
$this->client->buildUpdateRequest(
|
||||
Resources::SHIPMENTS,
|
||||
(string) $shipment->getId(),
|
||||
);
|
||||
|
||||
$this->client->addRequestData('tracking', $trackingCode);
|
||||
$this->client->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I try to view the summary of the (customer's latest cart)$/
|
||||
*/
|
||||
public function iTryToViewTheSummaryOfTheCustomersLatestCart(OrderInterface $cart): void
|
||||
{
|
||||
$this->client->show(Resources::ORDERS, $cart->getTokenValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I specify filter date to as :dateTime
|
||||
*/
|
||||
|
|
@ -978,6 +1016,105 @@ final class ManagingOrdersContext implements Context
|
|||
Assert::same($this->getTotalAsInt($subTotal), $orderItem['unitPrice'] * $orderItem['quantity'] + $unitPromotionAdjustments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the order has been successfully shipped
|
||||
*/
|
||||
public function iShouldBeNotifiedThatTheOrderHasBeenSuccessfullyShipped(): void
|
||||
{
|
||||
$response = $this->client->getLastResponse();
|
||||
Assert::true(
|
||||
$this->responseChecker->isUpdateSuccessful($response),
|
||||
'Order could not be shipped. Reason: ' . $response->getContent(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then it should have shipment in state shipped
|
||||
*/
|
||||
public function itShouldHaveShipmentInStateShipped(): void
|
||||
{
|
||||
$shipmentIri = $this->responseChecker->getValue(
|
||||
$this->client->show(Resources::ORDERS, $this->sharedStorage->get('order')->getTokenValue()),
|
||||
'shipments',
|
||||
)[0];
|
||||
|
||||
Assert::true(
|
||||
$this->responseChecker->hasValue($this->client->showByIri($shipmentIri['@id']), 'state', ShipmentInterface::STATE_SHIPPED),
|
||||
sprintf('Shipment for this order is not %s', ShipmentInterface::STATE_SHIPPED),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then this order should have order shipping state :orderShippingState
|
||||
*/
|
||||
public function thisOrderShouldHaveOrderShippingState(string $orderShippingState): void
|
||||
{
|
||||
$ordersResponse = $this->client->index(Resources::ORDERS);
|
||||
|
||||
Assert::true(
|
||||
$this->responseChecker->hasItemWithValue($ordersResponse, 'shippingState', strtolower($orderShippingState)),
|
||||
sprintf('Order does not have %s shipping state', $orderShippingState),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should not be able to ship this order
|
||||
*/
|
||||
public function iShouldNotBeAbleToShipThisOrder(): void
|
||||
{
|
||||
$order = $this->sharedStorage->get('order');
|
||||
|
||||
$this->client->applyTransition(
|
||||
Resources::SHIPMENTS,
|
||||
(string) $order->getShipments()->first()->getId(),
|
||||
ShipmentTransitions::TRANSITION_SHIP,
|
||||
);
|
||||
|
||||
Assert::false(
|
||||
$this->responseChecker->isUpdateSuccessful($this->client->getLastResponse()),
|
||||
'Order has been shipped, but should not.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be informed that the order does not exist
|
||||
*/
|
||||
public function iShouldBeInformedThatTheOrderDoesNotExist(): void
|
||||
{
|
||||
Assert::same(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'Not Found',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then there should be :count shipping address changes in the registry
|
||||
*/
|
||||
public function thereShouldBeCountShippingAddressChangesInTheRegistry(int $count): void
|
||||
{
|
||||
$order = $this->sharedStorage->get('order');
|
||||
$response = $this->client->subResourceIndex(
|
||||
Resources::ADDRESSES,
|
||||
Subresources::ADDRESSES_LOG_ENTRIES,
|
||||
(string) $order->getShippingAddress()->getId(),
|
||||
);
|
||||
Assert::same($this->responseChecker->countCollectionItems($response), $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then there should be :count billing address changes in the registry
|
||||
*/
|
||||
public function thereShouldBeCountBillingAddressChangesInTheRegistry(int $count): void
|
||||
{
|
||||
$order = $this->sharedStorage->get('order');
|
||||
$response = $this->client->subResourceIndex(
|
||||
Resources::ADDRESSES,
|
||||
Subresources::ADDRESSES_LOG_ENTRIES,
|
||||
(string) $order->getBillingAddress()->getId(),
|
||||
);
|
||||
Assert::same($this->responseChecker->countCollectionItems($response), $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $address
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Behat\Context\Api\Admin;
|
||||
|
||||
use ApiPlatform\Api\IriConverterInterface;
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Behat\Client\ApiClientInterface;
|
||||
use Sylius\Behat\Client\ResponseCheckerInterface;
|
||||
|
|
@ -23,6 +24,7 @@ use Sylius\Component\Core\Model\ChannelInterface;
|
|||
use Sylius\Component\Core\Model\CustomerInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Payment\PaymentTransitions;
|
||||
use Symfony\Component\HttpFoundation\Request as HttpRequest;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class ManagingPaymentsContext implements Context
|
||||
|
|
@ -31,6 +33,7 @@ final class ManagingPaymentsContext implements Context
|
|||
private ApiClientInterface $client,
|
||||
private ResponseCheckerInterface $responseChecker,
|
||||
private SectionAwareIriConverterInterface $sectionAwareIriConverter,
|
||||
private IriConverterInterface $iriConverter,
|
||||
private string $apiUrlPrefix,
|
||||
) {
|
||||
}
|
||||
|
|
@ -44,6 +47,34 @@ final class ManagingPaymentsContext implements Context
|
|||
$this->client->index(Resources::PAYMENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I go to the details of the first payment's order
|
||||
*/
|
||||
public function iGoToTheDetailsOfTheFirstPaymentSOrder(): void
|
||||
{
|
||||
$firstPayment = $this->responseChecker->getCollection($this->client->getLastResponse())[0];
|
||||
|
||||
/** @var OrderInterface $order */
|
||||
$order = $this->iriConverter->getResourceFromIri($firstPayment['order']);
|
||||
|
||||
$this->client->customItemAction(Resources::ORDERS, $order->getTokenValue(), HttpRequest::METHOD_GET, 'payments');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see the details of order :order
|
||||
*/
|
||||
public function iShouldSeeOrderWithDetails(OrderInterface $order): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->hasItemWithValue(
|
||||
$this->client->getLastResponse(),
|
||||
'order',
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($order, 'admin'),
|
||||
),
|
||||
sprintf('Order with number %s does not exist', $order->getNumber()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I complete the payment of order :order
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Behat\Context\Api\Admin;
|
||||
|
||||
use ApiPlatform\Api\IriConverterInterface;
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Behat\Client\ApiClientInterface;
|
||||
use Sylius\Behat\Client\ResponseCheckerInterface;
|
||||
use Sylius\Behat\Context\Api\Resources;
|
||||
use Sylius\Behat\Service\Converter\SectionAwareIriConverterInterface;
|
||||
use Sylius\Behat\Service\SharedStorageInterface;
|
||||
use Sylius\Component\Core\Repository\ProductAssociationRepositoryInterface;
|
||||
use Sylius\Component\Product\Model\ProductAssociationInterface;
|
||||
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
|
||||
use Sylius\Component\Product\Model\ProductInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
class ManagingProductAssociationsContext implements Context
|
||||
{
|
||||
public function __construct(
|
||||
private ApiClientInterface $client,
|
||||
private ResponseCheckerInterface $responseChecker,
|
||||
private IriConverterInterface $iriConverter,
|
||||
private SectionAwareIriConverterInterface $sectionAwareIriConverter,
|
||||
private ProductAssociationRepositoryInterface $associationRepository,
|
||||
private SharedStorageInterface $sharedStorage,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I (associate as "[^"]+") the (product "[^"]+") with the ("[^"]+" product)$/
|
||||
*/
|
||||
public function iAssociateAsTypeTheProductWithTheProduct(
|
||||
ProductAssociationTypeInterface $type,
|
||||
ProductInterface $owner,
|
||||
ProductInterface $product,
|
||||
): void {
|
||||
$this->iAssociateAsTypeTheProductWithTheProducts($type, $owner, [$product]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I (associate as "[^"]+") the (product "[^"]+") with the (products "[^"]+" and "[^"]+")$/
|
||||
*/
|
||||
public function iAssociateAsTypeTheProductWithTheProducts(
|
||||
ProductAssociationTypeInterface $type,
|
||||
ProductInterface $owner,
|
||||
array $products,
|
||||
): void {
|
||||
$associatedProductsData = [];
|
||||
/** @var ProductInterface $product */
|
||||
foreach ($products as $product) {
|
||||
$associatedProductsData[] = $this->iriConverter->getIriFromItem($product);
|
||||
}
|
||||
|
||||
$this->client->buildCreateRequest(Resources::PRODUCT_ASSOCIATIONS);
|
||||
$this->client->addRequestData('type', $this->iriConverter->getIriFromItem($type));
|
||||
$this->client->addRequestData('owner', $this->iriConverter->getIriFromItem($owner));
|
||||
$this->client->addRequestData('associatedProducts', $associatedProductsData);
|
||||
$this->client->create();
|
||||
|
||||
/** @var ProductAssociationInterface $association */
|
||||
$association = $this->associationRepository->findOneBy([
|
||||
'owner' => $owner,
|
||||
'type' => $type,
|
||||
]);
|
||||
|
||||
$this->sharedStorage->set('association', $association);
|
||||
$this->sharedStorage->set('product', $association->getOwner());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I add the (product "[^"]+") to (this product association)$/
|
||||
*/
|
||||
public function iAddTheProductToThisProductAssociation(
|
||||
ProductInterface $product,
|
||||
ProductAssociationInterface $association,
|
||||
): void {
|
||||
$this->client->buildUpdateRequest(Resources::PRODUCT_ASSOCIATIONS, (string) $association->getId());
|
||||
|
||||
$associatedProducts = [$this->iriConverter->getIriFromItem($product)];
|
||||
foreach ($association->getAssociatedProducts() as $associatedProduct) {
|
||||
$associatedProducts[] = $this->iriConverter->getIriFromItem($associatedProduct);
|
||||
}
|
||||
|
||||
$this->client->setRequestData(['associatedProducts' => $associatedProducts]);
|
||||
$this->client->update();
|
||||
|
||||
$this->sharedStorage->set('association', $association);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I change (this product association)'s product to the ("[^"]+" product)$/
|
||||
*/
|
||||
public function iChangeThisProductAssociationProductToProduct(
|
||||
ProductAssociationInterface $association,
|
||||
ProductInterface $product,
|
||||
): void {
|
||||
$this->client->buildUpdateRequest(Resources::PRODUCT_ASSOCIATIONS, (string) $association->getId());
|
||||
$this->client->addRequestData('associatedProducts', [$this->iriConverter->getIriFromItem($product)]);
|
||||
$this->client->update();
|
||||
|
||||
$this->sharedStorage->set('association', $association);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I remove the (product "[^"]+") from (this product association)$/
|
||||
*/
|
||||
public function iRemoveTheProductFromThisProductAssociation(
|
||||
ProductInterface $product,
|
||||
ProductAssociationInterface $association,
|
||||
): void {
|
||||
$this->client->buildUpdateRequest(Resources::PRODUCT_ASSOCIATIONS, (string) $association->getId());
|
||||
|
||||
$associatedProducts = [];
|
||||
foreach ($association->getAssociatedProducts() as $associatedProduct) {
|
||||
if ($associatedProduct->getCode() !== $product->getCode()) {
|
||||
$associatedProducts[] = $this->iriConverter->getIriFromItem($associatedProduct);
|
||||
}
|
||||
}
|
||||
|
||||
$this->client->setRequestData(['associatedProducts' => $associatedProducts]);
|
||||
$this->client->update();
|
||||
|
||||
$this->sharedStorage->set('association', $association);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this product) should have an (association "[^"]+")$/
|
||||
*/
|
||||
public function thisProductShouldHaveAnAssociation(
|
||||
ProductInterface $product,
|
||||
ProductAssociationTypeInterface $type,
|
||||
): void {
|
||||
$response = $this->client->show(Resources::PRODUCTS, $product->getCode());
|
||||
$associations = $this->responseChecker->getValue($response, 'associations');
|
||||
|
||||
$associationTypeIri = $this->sectionAwareIriConverter->getIriFromResourceInSection($type, 'admin');
|
||||
|
||||
foreach ($associations as $associationIri) {
|
||||
$response = $this->client->showByIri($associationIri);
|
||||
$productAssociationType = $this->responseChecker->getValue($response, 'type');
|
||||
|
||||
if ($associationTypeIri === $productAssociationType) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Product %s does not have an association of type %s',
|
||||
$product->getCode(),
|
||||
$type->getName()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this association) should only have (product "[^"]+")$/
|
||||
*/
|
||||
public function thisAssociationShouldOnlyHaveProduct(
|
||||
ProductAssociationInterface $association,
|
||||
ProductInterface $product,
|
||||
): void {
|
||||
$this->thisAssociationShouldHaveProducts($association, [$product]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this association) should have (products "[^"]+" and "[^"]+")$/
|
||||
*/
|
||||
public function thisAssociationShouldHaveProducts(
|
||||
ProductAssociationInterface $association,
|
||||
array $products,
|
||||
): void {
|
||||
$response = $this->client->show(Resources::PRODUCT_ASSOCIATIONS, (string) $association->getId());
|
||||
|
||||
$content = $this->responseChecker->getResponseContent($response);
|
||||
$associatedProducts = $content['associatedProducts'];
|
||||
|
||||
Assert::count($associatedProducts, count($products));
|
||||
|
||||
/** @var ProductInterface $product */
|
||||
foreach ($products as $product) {
|
||||
Assert::inArray(
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($product, 'admin'),
|
||||
$associatedProducts,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,12 +131,14 @@ final class ManagingProductImagesContext implements Context
|
|||
*/
|
||||
public function theProductShouldHaveAnImageWithType(ProductInterface $product, string $type): void
|
||||
{
|
||||
Assert::true($this->responseChecker->hasSubResourceWithValue(
|
||||
$this->client->show(Resources::PRODUCTS, $product->getCode()),
|
||||
'images',
|
||||
'type',
|
||||
$type,
|
||||
));
|
||||
Assert::true(
|
||||
$this->responseChecker->hasValuesInAnySubresourceObjectCollection(
|
||||
$this->client->show(Resources::PRODUCTS, $product->getCode()),
|
||||
'images',
|
||||
['type' => $type],
|
||||
),
|
||||
sprintf('Product %s does not have an image with %s type', $product->getName(), $type),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,12 +164,14 @@ final class ManagingProductImagesContext implements Context
|
|||
*/
|
||||
public function thisProductShouldNotHaveAnyImagesWithType(ProductInterface $product, string $type): void
|
||||
{
|
||||
Assert::false($this->responseChecker->hasSubResourceWithValue(
|
||||
$this->client->show(Resources::PRODUCTS, $product->getCode()),
|
||||
'images',
|
||||
'type',
|
||||
$type,
|
||||
));
|
||||
Assert::false(
|
||||
$this->responseChecker->hasValuesInAnySubresourceObjectCollection(
|
||||
$this->client->show(Resources::PRODUCTS, $product->getCode()),
|
||||
'images',
|
||||
['type' => $type],
|
||||
),
|
||||
sprintf('Product %s does not have an image with %s type', $product->getName(), $type),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ namespace Sylius\Behat\Context\Api\Admin;
|
|||
use ApiPlatform\Api\IriConverterInterface;
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Behat\Client\ApiClientInterface;
|
||||
use Sylius\Behat\Client\ResponseCheckerInterface;
|
||||
use Sylius\Behat\Context\Api\Resources;
|
||||
use Sylius\Behat\Service\SharedStorageInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductTaxonInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
|
|
@ -24,8 +26,47 @@ use Webmozart\Assert\Assert;
|
|||
|
||||
final class ManagingProductTaxonsContext implements Context
|
||||
{
|
||||
public function __construct(private ApiClientInterface $client, private IriConverterInterface $iriConverter)
|
||||
public function __construct(
|
||||
private ApiClientInterface $client,
|
||||
private IriConverterInterface $iriConverter,
|
||||
private ResponseCheckerInterface $responseChecker,
|
||||
private SharedStorageInterface $sharedStorage,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I am browsing the (\d+)(?:st|nd|rd|th) page of products from ("([^"]+)" taxon)$/
|
||||
* @When /^I go to the (\d+)(?:st|nd|rd|th) page of products from ("([^"]+)" taxon)$/
|
||||
*/
|
||||
public function iAmBrowsingThePageOfProductsFromTaxon(int $page, TaxonInterface $taxon): void
|
||||
{
|
||||
$this->iAmBrowsingProductsFromTaxon($taxon);
|
||||
$this->client->addFilter('page', $page);
|
||||
$this->client->filter();
|
||||
|
||||
$this->sharedStorage->set('response', $this->client->getLastResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I am browsing products from ("([^"]+)" taxon)$/
|
||||
*/
|
||||
public function iAmBrowsingProductsFromTaxon(TaxonInterface $taxon): void
|
||||
{
|
||||
$this->client->index(Resources::PRODUCT_TAXONS);
|
||||
$this->client->addFilter('taxon.code', $taxon->getCode());
|
||||
$this->client->addFilter('itemsPerPage', 10);
|
||||
$this->client->filter();
|
||||
|
||||
$this->sharedStorage->set('response', $this->client->getLastResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I set the position of :product to :position
|
||||
*/
|
||||
public function iSetThePositionOfProductTo(ProductInterface $product, string|int $position): void
|
||||
{
|
||||
$this->client->buildUpdateRequest(Resources::PRODUCT_TAXONS, (string) $product->getProductTaxons()->current()->getId());
|
||||
$this->client->updateRequestData(['position' => is_numeric($position) ? (int) $position : $position]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -84,6 +125,38 @@ final class ManagingProductTaxonsContext implements Context
|
|||
$this->client->delete(Resources::PRODUCT_TAXONS, (string) $productTaxon->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I sort this taxon's products :order by :field
|
||||
*/
|
||||
public function iSortProductsBy(string $order, string $field): void
|
||||
{
|
||||
$this->client->sort([$field => ManagingProductsContext::SORT_TYPES[$order]]);
|
||||
$this->sharedStorage->set('response', $this->client->getLastResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I save my new configuration
|
||||
*/
|
||||
public function iSaveMyNewConfiguration(): void
|
||||
{
|
||||
$this->client->update();
|
||||
$this->sharedStorage->set('response', $this->client->getLastResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the (first|last) product on the list within this taxon should have name "([^"]+)"$/
|
||||
*/
|
||||
public function theLastProductOnTheListWithinThisTaxonShouldHaveName(string $position, string $name): void
|
||||
{
|
||||
$productTaxons = $this->responseChecker->getCollection($this->sharedStorage->get('response'));
|
||||
$productTaxon = $position === 'last' ? end($productTaxons) : reset($productTaxons);
|
||||
|
||||
/** @var ProductInterface $product */
|
||||
$product = $this->iriConverter->getResourceFromIri($productTaxon['product']);
|
||||
|
||||
Assert::same($product->getTranslation()->getName(), $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that specifying a :part is required
|
||||
*/
|
||||
|
|
@ -105,4 +178,15 @@ final class ManagingProductTaxonsContext implements Context
|
|||
'Product taxons cannot be duplicated.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the position :position is invalid
|
||||
*/
|
||||
public function iShouldBeNotifiedThatThePositionIsInvalid(): void
|
||||
{
|
||||
Assert::contains(
|
||||
(string) $this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'The type of the "position" attribute must be "int", "string" given.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,12 @@ use Sylius\Component\Attribute\Model\AttributeValueInterface;
|
|||
use Sylius\Component\Core\Model\AdminUserInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductTaxonInterface;
|
||||
use Sylius\Component\Core\Model\ProductVariantInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Sylius\Component\Locale\Model\LocaleInterface;
|
||||
use Sylius\Component\Product\Model\ProductAssociationInterface;
|
||||
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
|
||||
use Sylius\Component\Product\Model\ProductAttributeInterface;
|
||||
use Sylius\Component\Product\Model\ProductOptionInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
|
@ -396,13 +401,21 @@ final class ManagingProductsContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @When I access :product product page
|
||||
* @When I access the :product product
|
||||
*/
|
||||
public function iAccessProductPage(ProductInterface $product): void
|
||||
public function iAccessTheProduct(ProductInterface $product): void
|
||||
{
|
||||
$this->client->show(Resources::PRODUCTS, $product->getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I choose :channel as a channel filter
|
||||
*/
|
||||
public function iChooseChannelAsAChannelFilter(ChannelInterface $channel): void
|
||||
{
|
||||
$this->client->addFilter('channel', $this->iriConverter->getIriFromResource($channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I save my changes to the images
|
||||
*/
|
||||
|
|
@ -411,6 +424,125 @@ final class ManagingProductsContext implements Context
|
|||
// Intentionally left blank
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I filter
|
||||
*/
|
||||
public function iFilter(): void
|
||||
{
|
||||
$this->client->filter();
|
||||
|
||||
$this->sharedStorage->set('response', $this->client->getLastResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see main taxon is :taxon
|
||||
*/
|
||||
public function iShouldSeeMainTaxonIs(TaxonInterface $taxon): void
|
||||
{
|
||||
Assert::same(
|
||||
$this->responseChecker->getValue($this->client->getLastResponse(), 'mainTaxon'),
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($taxon, 'admin'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see product taxon :taxon
|
||||
*/
|
||||
public function iShouldSeeProductTaxon(TaxonInterface $taxon): void
|
||||
{
|
||||
$product = $this->sharedStorage->get('product');
|
||||
Assert::isInstanceOf($product, ProductInterface::class);
|
||||
$productTaxon = $product->getProductTaxons()->filter(
|
||||
fn(ProductTaxonInterface $productTaxon) => $productTaxon->getTaxon()->getCode() === $taxon->getCode()
|
||||
)->first();
|
||||
Assert::isInstanceOf($productTaxon, ProductTaxonInterface::class);
|
||||
|
||||
Assert::true($this->responseChecker->hasValueInCollection(
|
||||
$this->client->getLastResponse(),
|
||||
'productTaxons',
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($productTaxon, 'admin'),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see option :productOption
|
||||
*/
|
||||
public function iShouldSeeOption(ProductOptionInterface $productOption): void
|
||||
{
|
||||
Assert::true($this->responseChecker->hasValueInCollection(
|
||||
$this->client->getLastResponse(),
|
||||
'options',
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($productOption, 'admin'),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see :count variants
|
||||
*/
|
||||
public function iShouldSeeVariants(int $count): void
|
||||
{
|
||||
Assert::count(
|
||||
$this->responseChecker->getResponseContent($this->client->getLastResponse())['variants'] ?? [],
|
||||
$count,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see the :variant variant
|
||||
*/
|
||||
public function iShouldSeeTheVariant(ProductVariantInterface $variant): void
|
||||
{
|
||||
Assert::true($this->responseChecker->hasValueInCollection(
|
||||
$this->client->getLastResponse(),
|
||||
'variants',
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($variant, 'admin'),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see product :field is :value
|
||||
* @Then I should see product's :field is :value
|
||||
*/
|
||||
public function iShouldSeeProductFieldIs(string $field, string $value): void
|
||||
{
|
||||
$this->assertResponseHasTranslationFieldWithValue($field, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see product's meta keyword(s) is/are :metaKeywords
|
||||
*/
|
||||
public function iShouldSeeProductMetaKeywordsAre(string $metaKeywords): void
|
||||
{
|
||||
$this->assertResponseHasTranslationFieldWithValue('metaKeywords', $metaKeywords);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see product's short description is :shortDescription
|
||||
*/
|
||||
public function iShouldSeeProductShortDescriptionIs(string $shortDescription): void
|
||||
{
|
||||
$this->assertResponseHasTranslationFieldWithValue('shortDescription', $shortDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see product association type :productAssociationType
|
||||
*/
|
||||
public function iShouldSeeProductAssociationType(ProductAssociationTypeInterface $productAssociationType): void
|
||||
{
|
||||
$associations = $this->responseChecker->getValue($this->client->getLastResponse(), 'associations');
|
||||
foreach ($associations as $associationIri) {
|
||||
/** @var ProductAssociationInterface $association */
|
||||
$association = $this->iriConverter->getResourceFromIri($associationIri);
|
||||
if ($association->getType()->getCode() === $productAssociationType->getCode()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Product association type "%s" not found.', $productAssociationType->getCode()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that it has been successfully created
|
||||
*/
|
||||
|
|
@ -486,9 +618,10 @@ final class ManagingProductsContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Then I should see a single product in the list
|
||||
* @Then I should see :count products in the list
|
||||
*/
|
||||
public function iShouldSeeProductsInTheList(int $count): void
|
||||
public function iShouldSeeProductsInTheList(int $count = 1): void
|
||||
{
|
||||
Assert::count($this->responseChecker->getCollection($this->client->getLastResponse()), $count);
|
||||
}
|
||||
|
|
@ -562,7 +695,7 @@ final class ManagingProductsContext implements Context
|
|||
$this->responseChecker->hasItemWithValues($this->client->getLastResponse(), [
|
||||
'product' => $this->sectionAwareIriConverter->getIriFromResourceInSection($product, 'admin'),
|
||||
'taxon' => $this->sectionAwareIriConverter->getIriFromResourceInSection($taxon, 'admin'),
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -612,11 +745,31 @@ final class ManagingProductsContext implements Context
|
|||
*/
|
||||
public function theFirstProductOnTheListShouldHave(string $field, string $value): void
|
||||
{
|
||||
$response = $this->getLastResponse();
|
||||
$products = $this->responseChecker->getCollection($this->getLastResponse());
|
||||
|
||||
$products = $this->responseChecker->getCollection($response);
|
||||
Assert::same($this->getFieldValueOfProduct($products[0], $field), $value);
|
||||
}
|
||||
|
||||
Assert::same($this->getFieldValueOfFirstProduct($products[0], $field), $value);
|
||||
/**
|
||||
* @Then the last product on the list should have name :name
|
||||
*/
|
||||
public function theLastProductOnTheListShouldHaveName(string $name): void
|
||||
{
|
||||
$products = $this->responseChecker->getCollection($this->getLastResponse());
|
||||
|
||||
Assert::same($this->getFieldValueOfProduct(end($products), 'name'), $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the (first|last) product on the list shouldn't have a name$/
|
||||
*/
|
||||
public function theProductOnTheListShouldNotHaveAName(string $position): void
|
||||
{
|
||||
$products = $this->responseChecker->getCollection($this->getLastResponse());
|
||||
|
||||
$product = $position === 'last' ? end($products) : reset($products);
|
||||
|
||||
Assert::null($this->getFieldValueOfProduct($product, 'name'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -820,6 +973,17 @@ final class ManagingProductsContext implements Context
|
|||
Assert::notEmpty($this->responseChecker->getValue($this->client->getLastResponse(), 'images'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see attribute :attribute with value :value in :locale locale
|
||||
*/
|
||||
public function iShouldSeeAttributeWithValueInLocale(
|
||||
ProductAttributeInterface $attribute,
|
||||
string $value,
|
||||
LocaleInterface $locale,
|
||||
): void {
|
||||
$this->hasAttributeWithValueInLastResponse($attribute, $value, $locale->getCode());
|
||||
}
|
||||
|
||||
private function getAdminLocaleCode(): string
|
||||
{
|
||||
/** @var AdminUserInterface $adminUser */
|
||||
|
|
@ -830,14 +994,14 @@ final class ManagingProductsContext implements Context
|
|||
return $this->responseChecker->getValue($response, 'localeCode');
|
||||
}
|
||||
|
||||
private function getFieldValueOfFirstProduct(array $product, string $field): ?string
|
||||
private function getFieldValueOfProduct(array $product, string $field): ?string
|
||||
{
|
||||
if ($field === 'code') {
|
||||
return $product['code'];
|
||||
}
|
||||
|
||||
if ($field === 'name') {
|
||||
return $product['translations'][$this->getAdminLocaleCode()]['name'];
|
||||
return $product['translations'][$this->getAdminLocaleCode()]['name'] ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -934,4 +1098,12 @@ final class ManagingProductsContext implements Context
|
|||
|
||||
Assert::same((string) $value, $expectedValue);
|
||||
}
|
||||
|
||||
private function assertResponseHasTranslationFieldWithValue(string $field, string $value): void
|
||||
{
|
||||
Assert::same(
|
||||
$this->responseChecker->getTranslationValue($this->client->getLastResponse(), $field),
|
||||
$value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -836,7 +836,7 @@ final class ManagingPromotionsContext implements Context
|
|||
$returnedPromotion = current($this->responseChecker->getCollectionItemsWithValue(
|
||||
$this->client->getLastResponse(),
|
||||
'code',
|
||||
$promotion->getCode()
|
||||
$promotion->getCode(),
|
||||
));
|
||||
|
||||
Assert::same(
|
||||
|
|
|
|||
|
|
@ -59,6 +59,19 @@ final class ManagingShipmentsContext implements Context
|
|||
$this->client->addFilter('state', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I move to the details of first shipment's order
|
||||
*/
|
||||
public function iMoveToDetailsOfFirstShipment(): void
|
||||
{
|
||||
$firstShipment = $this->responseChecker->getCollection($this->client->getLastResponse())[0];
|
||||
|
||||
/** @var OrderInterface $order */
|
||||
$order = $this->iriConverter->getResourceFromIri($firstShipment['order']);
|
||||
|
||||
$this->client->customItemAction(Resources::ORDERS, $order->getTokenValue(), HttpRequest::METHOD_GET, 'shipments');
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I choose :channel as a channel filter
|
||||
*/
|
||||
|
|
@ -290,6 +303,21 @@ final class ManagingShipmentsContext implements Context
|
|||
Assert::same($productUnitsCounter, $amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see the details of order :order
|
||||
*/
|
||||
public function iShouldSeeOrderWithDetails(OrderInterface $order): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->hasItemWithValue(
|
||||
$this->client->getLastResponse(),
|
||||
'order',
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($order, 'admin'),
|
||||
),
|
||||
sprintf('Order with number %s does not exist', $order->getNumber()),
|
||||
);
|
||||
}
|
||||
|
||||
private function isShipmentForOrder(OrderInterface $order): bool
|
||||
{
|
||||
return $this->responseChecker->hasItemWithValue(
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ use Sylius\Behat\Context\Api\Resources;
|
|||
use Sylius\Behat\Service\Converter\SectionAwareIriConverterInterface;
|
||||
use Sylius\Behat\Service\SharedStorageInterface;
|
||||
use Sylius\Component\Addressing\Model\ZoneInterface;
|
||||
use Sylius\Component\Core\Formatter\StringInflector;
|
||||
use Sylius\Component\Core\Model\AdminUserInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ShippingMethodInterface;
|
||||
use Sylius\Component\Shipping\Calculator\DefaultCalculators;
|
||||
use Symfony\Component\HttpFoundation\Request as HttpRequest;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
|
|
@ -63,6 +65,82 @@ final class ManagingShippingMethodsContext implements Context
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I add the :rule rule configured with :weight
|
||||
*/
|
||||
public function iAddTheRuleConfiguredWithWeight(string $rule, int $weight): void
|
||||
{
|
||||
$type = StringInflector::nameToLowercaseCode($rule);
|
||||
$this->client->addRequestData('rules', [[
|
||||
'type' => $type,
|
||||
'configuration' => [
|
||||
'weight' => $weight,
|
||||
],
|
||||
]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I add the "Total weight greater than or equal" rule configured with invalid data
|
||||
*/
|
||||
public function iAddTheTotalWeightGreaterThanOrEqualRuleConfiguredWithInvalidData(): void
|
||||
{
|
||||
$this->client->addRequestData('rules', [
|
||||
[
|
||||
'type' => 'total_weight_greater_than_or_equal',
|
||||
'configuration' => [
|
||||
'weight' => true,
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I add the "([^"]+)" rule configured with (?:€|£|\$)([^"]+) for ("[^"]+" channel)$/
|
||||
*/
|
||||
public function iAddTheRuleConfiguredWithForChannel(string $rule, int $value, ChannelInterface $channel): void
|
||||
{
|
||||
match ($rule) {
|
||||
'Items total less than or equal' => $this->client->addRequestData('rules', [
|
||||
[
|
||||
'type' => 'order_total_less_than_or_equal',
|
||||
'configuration' => [
|
||||
$channel->getCode() => [
|
||||
'amount' => $value,
|
||||
],
|
||||
],
|
||||
],
|
||||
]),
|
||||
'Items total greater than or equal' => $this->client->addRequestData('rules', [
|
||||
[
|
||||
'type' => 'order_total_greater_than_or_equal',
|
||||
'configuration' => [
|
||||
$channel->getCode() => [
|
||||
'amount' => $value,
|
||||
],
|
||||
],
|
||||
],
|
||||
]),
|
||||
default => throw new \InvalidArgumentException('Unsupported shipping method rule'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I add the "Items total less than or equal" rule configured with invalid data for ("[^"]+" channel)$/
|
||||
*/
|
||||
public function iAddTheItemsTotalLessThanOrEqualRuleConfiguredWithInvalidData(ChannelInterface $channel): void
|
||||
{
|
||||
$this->client->addRequestData('rules', [
|
||||
[
|
||||
'type' => 'order_total_greater_than_or_equal',
|
||||
'configuration' => [
|
||||
$channel->getCode() => [
|
||||
'amount' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I change my locale to :localeCode
|
||||
*/
|
||||
|
|
@ -123,6 +201,29 @@ final class ManagingShippingMethodsContext implements Context
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I do not specify amount for :calculatorName calculator
|
||||
*/
|
||||
public function iDoNotSpecifyAmountForCalculator(string $calculatorName): void
|
||||
{
|
||||
match ($calculatorName) {
|
||||
'Flat rate per shipment' => $this->client->addRequestData('calculator', DefaultCalculators::FLAT_RATE),
|
||||
'Flat rate per unit' => $this->client->addRequestData('calculator', DefaultCalculators::PER_UNIT_RATE),
|
||||
'default' => throw new \InvalidArgumentException('Unsupported calculator name'),
|
||||
};
|
||||
|
||||
$channelCode = $this->sharedStorage->get('channel')->getCode();
|
||||
$this->client->addRequestData('configuration', [$channelCode => ['amount' => null]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I remove its zone
|
||||
*/
|
||||
public function iRemoveItsZone(): void
|
||||
{
|
||||
$this->client->replaceRequestData('zone', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I try to show :shippingMethod shipping method
|
||||
*/
|
||||
|
|
@ -310,6 +411,17 @@ final class ManagingShippingMethodsContext implements Context
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the shipping method :name should not appear in the registry
|
||||
*/
|
||||
public function theShippingMethodShouldNotAppearInTheRegistry(string $name): void
|
||||
{
|
||||
Assert::false(
|
||||
$this->responseChecker->hasItemWithTranslation($this->client->index(Resources::SHIPPING_METHODS), 'en_US', 'name', $name),
|
||||
sprintf('Shipping method with name %s exists', $name),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this shipping method) should still be in the registry$/
|
||||
*/
|
||||
|
|
@ -527,6 +639,17 @@ final class ManagingShippingMethodsContext implements Context
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the zone is required
|
||||
*/
|
||||
public function iShouldBeNotifiedThatZoneHasToBeIriAndCannotBeNull(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'Expected IRI or nested document for attribute "zone", "NULL" given.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then shipping method with :element :value should not be added
|
||||
*/
|
||||
|
|
@ -578,6 +701,72 @@ final class ManagingShippingMethodsContext implements Context
|
|||
// Intentionally left blank
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then shipping method :shippingMethod should still have code :code
|
||||
*/
|
||||
public function shippingMethodShouldStillHaveCode(ShippingMethodInterface $shippingMethod, string $code): void
|
||||
{
|
||||
Assert::same(
|
||||
$this->responseChecker->getValue($this->client->show(Resources::SHIPPING_METHODS, $shippingMethod->getCode()), 'code'),
|
||||
$code,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that amount for :channel channel should not be blank
|
||||
*/
|
||||
public function iShouldBeNotifiedThatAmountForChannelShouldNotBeBlank(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
sprintf('configuration[%s][amount]: This value should not be blank.', $channel->getCode()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that code needs to contain only specific symbols
|
||||
*/
|
||||
public function iShouldBeNotifiedThatCodeNeedsToContainOnlySpecificSymbols(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'code: Shipping method code can only be comprised of letters, numbers, dashes and underscores.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that shipping charge for :channel channel cannot be lower than 0
|
||||
*/
|
||||
public function iShouldBeNotifiedThatShippingChargeForChannelCannotBeLowerThan0(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
sprintf('configuration[%s][amount]: Shipping charge cannot be lower than 0.', $channel->getCode()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the weight rule has an invalid configuration
|
||||
*/
|
||||
public function iShouldBeNotifiedThatTheWeightRuleHasAnInvalidConfiguration(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'configuration[weight]: This value should be of type numeric.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the amount rule has an invalid configuration in :channel channel
|
||||
*/
|
||||
public function iShouldBeNotifiedThatTheAmountRuleHasAnInvalidConfigurationInChannel(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
sprintf('configuration[%s][amount]: This value should be of type numeric.', $channel->getCode()),
|
||||
);
|
||||
}
|
||||
|
||||
private function getAdminLocaleCode(): string
|
||||
{
|
||||
/** @var AdminUserInterface $adminUser */
|
||||
|
|
|
|||
|
|
@ -124,11 +124,10 @@ final class ManagingTaxonImagesContext implements Context
|
|||
*/
|
||||
public function thisTaxonShouldHaveAnImageWithType(TaxonInterface $taxon, string $type): void
|
||||
{
|
||||
Assert::true($this->responseChecker->hasSubResourceWithValue(
|
||||
Assert::true($this->responseChecker->hasValuesInAnySubresourceObjectCollection(
|
||||
$this->client->show(Resources::TAXONS, $taxon->getCode()),
|
||||
'images',
|
||||
'type',
|
||||
$type,
|
||||
['type' => $type],
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -138,11 +137,10 @@ final class ManagingTaxonImagesContext implements Context
|
|||
*/
|
||||
public function thisTaxonShouldNotHaveAnyImagesWithType(TaxonInterface $taxon, string $type): void
|
||||
{
|
||||
Assert::false($this->responseChecker->hasSubResourceWithValue(
|
||||
Assert::false($this->responseChecker->hasValuesInAnySubresourceObjectCollection(
|
||||
$this->client->show(Resources::TAXONS, $taxon->getCode()),
|
||||
'images',
|
||||
'type',
|
||||
$type,
|
||||
['type' => $type],
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ final class Resources
|
|||
|
||||
public const PRODUCT_ASSOCIATION_TYPES = 'product-association-types';
|
||||
|
||||
public const PRODUCT_ASSOCIATIONS = 'product-associations';
|
||||
|
||||
public const PRODUCT_ATTRIBUTES = 'product-attributes';
|
||||
|
||||
public const PRODUCT_IMAGES = 'product-images';
|
||||
|
|
|
|||
|
|
@ -37,8 +37,15 @@ final class ProductAttributeContext implements Context
|
|||
public function iShouldSeeTheProductAttributeWithValue(string $attributeName, string $expectedAttribute): void
|
||||
{
|
||||
$attribute = $this->getAttributeByName($attributeName);
|
||||
$attributeValue = $attribute['value'];
|
||||
|
||||
Assert::same($attribute['value'], $expectedAttribute);
|
||||
if (is_array($attributeValue)) {
|
||||
Assert::inArray($expectedAttribute, $attributeValue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Assert::same($attributeValue, $expectedAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -71,6 +78,14 @@ final class ProductAttributeContext implements Context
|
|||
Assert::inArray($expectedAttribute, $attribute['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should not see the product attribute :attributeName
|
||||
*/
|
||||
public function iShouldNotSeeTheProductAttribute(string $attributeName): void
|
||||
{
|
||||
Assert::false($this->hasAttributeByName($attributeName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should (also) see the product attribute :attributeName with date :expectedAttribute
|
||||
*/
|
||||
|
|
@ -113,6 +128,17 @@ final class ProductAttributeContext implements Context
|
|||
Assert::same($attribute['name'], $name);
|
||||
}
|
||||
|
||||
private function hasAttributeByName(string $name): bool
|
||||
{
|
||||
foreach ($this->getAttributes() as $attribute) {
|
||||
if ($attribute['name'] === $name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getAttributeByName(string $name): array
|
||||
{
|
||||
foreach ($this->getAttributes() as $attribute) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ final class Subresources
|
|||
{
|
||||
public const PROMOTION_COUPONS = 'coupons';
|
||||
|
||||
public const ADDRESSES_LOG_ENTRIES = 'log-entries';
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ final class CartContext implements Context
|
|||
* @Given /^I (?:have|had) (product "[^"]+") in the (cart)$/
|
||||
* @Given /^I have (product "[^"]+") added to the (cart)$/
|
||||
* @Given /^the (?:customer|visitor) has (product "[^"]+") in the (cart)$/
|
||||
* @Given /^the (?:customer|visitor) added ("[^"]+" product) to the (cart)$/
|
||||
* @When /^the (?:customer|visitor) try to add (product "[^"]+") in the customer (cart)$/
|
||||
*/
|
||||
public function iAddedProductToTheCart(ProductInterface $product, ?string $tokenValue): void
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
namespace Sylius\Behat\Context\Setup;
|
||||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Behat\Step\Given;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Sylius\Behat\Service\Setter\ChannelContextSetterInterface;
|
||||
use Sylius\Behat\Service\SharedStorageInterface;
|
||||
|
|
@ -26,10 +27,12 @@ use Sylius\Component\Core\Model\ShopBillingData;
|
|||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Sylius\Component\Core\Test\Services\DefaultChannelFactoryInterface;
|
||||
use Sylius\Component\Locale\Model\LocaleInterface;
|
||||
use Sylius\Component\Resource\Repository\RepositoryInterface;
|
||||
|
||||
final class ChannelContext implements Context
|
||||
{
|
||||
/**
|
||||
* @param ChannelRepositoryInterface<ChannelInterface> $channelRepository
|
||||
*/
|
||||
public function __construct(
|
||||
private SharedStorageInterface $sharedStorage,
|
||||
private ChannelContextSetterInterface $channelContextSetter,
|
||||
|
|
@ -37,7 +40,6 @@ final class ChannelContext implements Context
|
|||
private DefaultChannelFactoryInterface $defaultChannelFactory,
|
||||
private ChannelRepositoryInterface $channelRepository,
|
||||
private ObjectManager $channelManager,
|
||||
private RepositoryInterface $localeRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ final class ChannelContext implements Context
|
|||
/**
|
||||
* @Given the store operates on a single channel in "United States"
|
||||
*/
|
||||
public function storeOperatesOnASingleChannelInUnitedStates()
|
||||
public function storeOperatesOnASingleChannelInUnitedStates(): void
|
||||
{
|
||||
$defaultData = $this->unitedStatesChannelFactory->create();
|
||||
|
||||
|
|
@ -78,9 +80,9 @@ final class ChannelContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given the store operates on a single channel in the "United States" named :channelIdentifier
|
||||
* @Given the store operates on a single channel in the "United States" named :channelName
|
||||
*/
|
||||
public function storeOperatesOnASingleChannelInTheUnitedStatesNamed(string $channelName)
|
||||
public function storeOperatesOnASingleChannelInTheUnitedStatesNamed(string $channelName): void
|
||||
{
|
||||
$channelCode = StringInflector::nameToLowercaseCode($channelName);
|
||||
$defaultData = $this->unitedStatesChannelFactory->create($channelCode, $channelName);
|
||||
|
|
@ -93,7 +95,7 @@ final class ChannelContext implements Context
|
|||
* @Given the store operates on a single channel
|
||||
* @Given the store operates on a single channel in :currencyCode currency
|
||||
*/
|
||||
public function storeOperatesOnASingleChannel($currencyCode = null)
|
||||
public function storeOperatesOnASingleChannel(?string $currencyCode = null): void
|
||||
{
|
||||
$defaultData = $this->defaultChannelFactory->create(null, null, $currencyCode);
|
||||
|
||||
|
|
@ -137,7 +139,7 @@ final class ChannelContext implements Context
|
|||
/**
|
||||
* @Given the channel :channel is enabled
|
||||
*/
|
||||
public function theChannelIsEnabled(ChannelInterface $channel)
|
||||
public function theChannelIsEnabled(ChannelInterface $channel): void
|
||||
{
|
||||
$this->changeChannelState($channel, true);
|
||||
}
|
||||
|
|
@ -146,7 +148,7 @@ final class ChannelContext implements Context
|
|||
* @Given the channel :channel is disabled
|
||||
* @Given the channel :channel has been disabled
|
||||
*/
|
||||
public function theChannelIsDisabled(ChannelInterface $channel)
|
||||
public function theChannelIsDisabled(ChannelInterface $channel): void
|
||||
{
|
||||
$this->changeChannelState($channel, false);
|
||||
}
|
||||
|
|
@ -154,7 +156,7 @@ final class ChannelContext implements Context
|
|||
/**
|
||||
* @Given /^the (channel "[^"]+") has showing the lowest price of discounted products (enabled|disabled)$/
|
||||
*/
|
||||
public function theChannelHasShowingTheLowestPriceOfDiscountedProducts(ChannelInterface $channel, string $visible)
|
||||
public function theChannelHasShowingTheLowestPriceOfDiscountedProducts(ChannelInterface $channel, string $visible): void
|
||||
{
|
||||
$channel->getChannelPriceHistoryConfig()->setLowestPriceForDiscountedProductsVisible($visible === 'enabled');
|
||||
|
||||
|
|
@ -164,7 +166,7 @@ final class ChannelContext implements Context
|
|||
/**
|
||||
* @Given channel :channel has been deleted
|
||||
*/
|
||||
public function iChannelHasBeenDeleted(ChannelInterface $channel)
|
||||
public function iChannelHasBeenDeleted(ChannelInterface $channel): void
|
||||
{
|
||||
$this->channelRepository->remove($channel);
|
||||
}
|
||||
|
|
@ -172,7 +174,7 @@ final class ChannelContext implements Context
|
|||
/**
|
||||
* @Given /^(its) default tax zone is (zone "([^"]+)")$/
|
||||
*/
|
||||
public function itsDefaultTaxRateIs(ChannelInterface $channel, ZoneInterface $defaultTaxZone)
|
||||
public function itsDefaultTaxRateIs(ChannelInterface $channel, ZoneInterface $defaultTaxZone): void
|
||||
{
|
||||
$channel->setDefaultTaxZone($defaultTaxZone);
|
||||
$this->channelManager->flush();
|
||||
|
|
@ -182,7 +184,7 @@ final class ChannelContext implements Context
|
|||
* @Given /^(this channel) has contact email set as "([^"]+)"$/
|
||||
* @Given /^(this channel) has no contact email set$/
|
||||
*/
|
||||
public function thisChannelHasContactEmailSetAs(ChannelInterface $channel, $contactEmail = null)
|
||||
public function thisChannelHasContactEmailSetAs(ChannelInterface $channel, ?string $contactEmail = null): void
|
||||
{
|
||||
$channel->setContactEmail($contactEmail);
|
||||
$this->channelManager->flush();
|
||||
|
|
@ -191,7 +193,7 @@ final class ChannelContext implements Context
|
|||
/**
|
||||
* @Given /^on (this channel) shipping step is skipped if only a single shipping method is available$/
|
||||
*/
|
||||
public function onThisChannelShippingStepIsSkippedIfOnlyASingleShippingMethodIsAvailable(ChannelInterface $channel)
|
||||
public function onThisChannelShippingStepIsSkippedIfOnlyASingleShippingMethodIsAvailable(ChannelInterface $channel): void
|
||||
{
|
||||
$channel->setSkippingShippingStepAllowed(true);
|
||||
|
||||
|
|
@ -203,7 +205,7 @@ final class ChannelContext implements Context
|
|||
*/
|
||||
public function onThisChannelPaymentStepIsSkippedIfOnlyASinglePaymentMethodIsAvailable(
|
||||
ChannelInterface $channel,
|
||||
) {
|
||||
): void {
|
||||
$channel->setSkippingPaymentStepAllowed(true);
|
||||
|
||||
$this->channelManager->flush();
|
||||
|
|
@ -368,10 +370,7 @@ final class ChannelContext implements Context
|
|||
$this->channelManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $state
|
||||
*/
|
||||
private function changeChannelState(ChannelInterface $channel, $state)
|
||||
private function changeChannelState(ChannelInterface $channel, bool $state): void
|
||||
{
|
||||
$channel->setEnabled($state);
|
||||
$this->channelManager->flush();
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ use Sylius\Component\Resource\Factory\FactoryInterface;
|
|||
use Sylius\Component\Resource\Repository\RepositoryInterface;
|
||||
use Sylius\Component\Shipping\Repository\ShippingMethodRepositoryInterface;
|
||||
use Sylius\Component\Shipping\ShipmentTransitions;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class OrderContext implements Context
|
||||
|
|
@ -64,6 +65,7 @@ final class OrderContext implements Context
|
|||
private ProductVariantResolverInterface $variantResolver,
|
||||
private OrderItemQuantityModifierInterface $itemQuantityModifier,
|
||||
private ObjectManager $objectManager,
|
||||
private ClockInterface $clock,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +560,7 @@ final class OrderContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given /^(\d+) customers have added products to the cart for total of ("[^"]+")$/
|
||||
* @Given /^(\d+) new customers have added products to the cart for total of ("[^"]+")$/
|
||||
*/
|
||||
public function customersHaveAddedProductsToTheCartForTotalOf(int $numberOfCustomers, int $total): void
|
||||
{
|
||||
|
|
@ -589,7 +591,7 @@ final class OrderContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given /^(\d+) (?:|more )customers have placed (\d+) orders for total of ("[^"]+")$/
|
||||
* @Given /^(\d+) (?:|more )new customers have placed (\d+) orders for total of ("[^"]+")$/
|
||||
*/
|
||||
public function customersHavePlacedOrdersForTotalOf(int $numberOfCustomers, int $numberOfOrders, int $total): void
|
||||
{
|
||||
|
|
@ -597,7 +599,7 @@ final class OrderContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given /^(\d+) customers have fulfilled (\d+) orders placed for total of ("[^"]+")$/
|
||||
* @Given /^(\d+) new customers have fulfilled (\d+) orders placed for total of ("[^"]+")$/
|
||||
*/
|
||||
public function customersHaveFulfilledOrdersPlacedForTotalOf(
|
||||
int $numberOfCustomers,
|
||||
|
|
@ -608,7 +610,7 @@ final class OrderContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given /^(\d+) (?:|more )customers have placed (\d+) orders for total of ("[^"]+") mostly ("[^"]+" product)$/
|
||||
* @Given /^(\d+) (?:|more )new customers have placed (\d+) orders for total of ("[^"]+") mostly ("[^"]+" product)$/
|
||||
*/
|
||||
public function customersHavePlacedOrdersForTotalOfMostlyProduct(
|
||||
int $numberOfCustomers,
|
||||
|
|
@ -620,7 +622,7 @@ final class OrderContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given /^(\d+) (?:|more )customers have fulfilled (\d+) orders placed for total of ("[^"]+") mostly ("[^"]+" product)$/
|
||||
* @Given /^(\d+) (?:|more )new customers have fulfilled (\d+) orders placed for total of ("[^"]+") mostly ("[^"]+" product)$/
|
||||
*/
|
||||
public function customersHaveFulfilledOrdersPlacedForTotalOfMostlyProduct(
|
||||
int $numberOfCustomers,
|
||||
|
|
@ -632,7 +634,7 @@ final class OrderContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given /^(\d+) (?:|more )customers have paid (\d+) orders placed for total of ("[^"]+")$/
|
||||
* @Given /^(\d+) (?:|more )new customers have paid (\d+) orders placed for total of ("[^"]+")$/
|
||||
*/
|
||||
public function moreCustomersHavePaidOrdersPlacedForTotalOf(
|
||||
int $numberOfCustomers,
|
||||
|
|
@ -918,6 +920,8 @@ final class OrderContext implements Context
|
|||
$customer->setFirstname('John');
|
||||
$customer->setLastname('Doe' . $i);
|
||||
|
||||
$customer->setCreatedAt($this->clock->now());
|
||||
|
||||
$customers[] = $customer;
|
||||
|
||||
$this->customerRepository->add($customer);
|
||||
|
|
@ -1020,6 +1024,8 @@ final class OrderContext implements Context
|
|||
$this->shipOrder($order);
|
||||
}
|
||||
|
||||
$order->setCheckoutCompletedAt($this->clock->now());
|
||||
|
||||
$this->objectManager->persist($order);
|
||||
$this->sharedStorage->set('order', $order);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,6 +140,8 @@ final class ProductAssociationContext implements Context
|
|||
$product->addAssociation($productAssociation);
|
||||
|
||||
$this->productAssociationRepository->add($productAssociation);
|
||||
|
||||
$this->sharedStorage->set('product_association', $productAssociation);
|
||||
}
|
||||
|
||||
private function addProductAssociationTypeTranslation(
|
||||
|
|
|
|||
|
|
@ -423,6 +423,59 @@ final class ProductAttributeContext implements Context
|
|||
$this->objectManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^(this product attribute)'s value changed from "([^"]+)" to "([^"]+)"$/
|
||||
*/
|
||||
public function thisAttributeValueChangedFromTo(
|
||||
ProductAttributeInterface $attribute,
|
||||
string $from,
|
||||
string $to,
|
||||
): void {
|
||||
$configuration = $attribute->getConfiguration();
|
||||
$choices = $configuration['choices'] ?? [];
|
||||
|
||||
foreach ($choices as $uuid => $choice) {
|
||||
foreach ($choice as $localeCode => $item) {
|
||||
if ($item === $from) {
|
||||
$choices[$uuid][$localeCode] = $to;
|
||||
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$configuration['choices'] = $choices;
|
||||
$attribute->setConfiguration($configuration);
|
||||
|
||||
$this->objectManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^(this product attribute)'s value "([^"]+)" has been removed$/
|
||||
*/
|
||||
public function thisAttributeValueHasBeenRemoved(
|
||||
ProductAttributeInterface $attribute,
|
||||
string $value,
|
||||
): void {
|
||||
$configuration = $attribute->getConfiguration();
|
||||
$choices = $configuration['choices'] ?? [];
|
||||
|
||||
foreach ($choices as $uuid => $choice) {
|
||||
foreach ($choice as $item) {
|
||||
if ($value === $item) {
|
||||
unset($choices[$uuid]);
|
||||
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$configuration['choices'] = $choices;
|
||||
$attribute->setConfiguration($configuration);
|
||||
|
||||
$this->objectManager->flush();
|
||||
}
|
||||
|
||||
private function createProductAttribute(
|
||||
string $type,
|
||||
string $name,
|
||||
|
|
|
|||
|
|
@ -15,10 +15,14 @@ namespace Sylius\Behat\Context\Transform;
|
|||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class ChannelContext implements Context
|
||||
{
|
||||
/**
|
||||
* @param ChannelRepositoryInterface<ChannelInterface> $channelRepository
|
||||
*/
|
||||
public function __construct(private ChannelRepositoryInterface $channelRepository)
|
||||
{
|
||||
}
|
||||
|
|
@ -29,7 +33,7 @@ final class ChannelContext implements Context
|
|||
* @Transform /^channel to "([^"]+)"$/
|
||||
* @Transform :channel
|
||||
*/
|
||||
public function getChannelByName($channelName)
|
||||
public function getChannelByName(string $channelName)
|
||||
{
|
||||
$channels = $this->channelRepository->findByName($channelName);
|
||||
|
||||
|
|
@ -44,8 +48,10 @@ final class ChannelContext implements Context
|
|||
|
||||
/**
|
||||
* @Transform all channels
|
||||
*
|
||||
* @return array<ChannelInterface>
|
||||
*/
|
||||
public function getAllChannels()
|
||||
public function getAllChannels(): array
|
||||
{
|
||||
return $this->channelRepository->findAll();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ final class ProductAssociationTypeContext implements Context
|
|||
|
||||
/**
|
||||
* @Transform /^association "([^"]+)"$/
|
||||
* @Transform /^associate as "([^"]+)"$/
|
||||
* @Transform :productAssociationType
|
||||
*/
|
||||
public function getProductAssociationTypeByName($productAssociationTypeName)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Sylius\Behat\Context\Ui\Admin;
|
|||
use Behat\Behat\Context\Context;
|
||||
use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException;
|
||||
use Sylius\Behat\Page\Admin\DashboardPageInterface;
|
||||
use Sylius\Component\Core\Formatter\StringInflector;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class DashboardContext implements Context
|
||||
|
|
@ -28,8 +28,9 @@ final class DashboardContext implements Context
|
|||
/**
|
||||
* @Given I am on the administration dashboard
|
||||
* @When I (try to )open administration dashboard
|
||||
* @When I (try to )view statistics
|
||||
*/
|
||||
public function iOpenAdministrationDashboard(): void
|
||||
public function iViewStatistics(): void
|
||||
{
|
||||
try {
|
||||
$this->dashboardPage->open();
|
||||
|
|
@ -38,17 +39,44 @@ final class DashboardContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @When I open administration dashboard for :name channel
|
||||
* @When I view statistics for :channel channel
|
||||
*
|
||||
* @throws UnexpectedPageException
|
||||
*/
|
||||
public function iOpenAdministrationDashboardForChannel($name)
|
||||
public function iViewStatisticsForChannel(ChannelInterface $channel): void
|
||||
{
|
||||
$this->dashboardPage->open(['channel' => StringInflector::nameToLowercaseCode($name)]);
|
||||
$this->dashboardPage->open(['channel' => $channel->getCode()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I view statistics for ("[^"]+" channel) and (current|previous|next) year split by (month|day)$/
|
||||
*
|
||||
* @throws UnexpectedPageException
|
||||
*/
|
||||
public function iViewStatisticsForChannelAndYear(
|
||||
ChannelInterface $channel,
|
||||
string $period,
|
||||
string $interval,
|
||||
): void {
|
||||
$this->dashboardPage->open(['channel' => $channel->getCode()]);
|
||||
|
||||
match ($interval) {
|
||||
'month' => $this->dashboardPage->chooseYearSplitByMonthsInterval(),
|
||||
'day' => $this->dashboardPage->chooseMonthSplitByDaysInterval(),
|
||||
default => throw new \InvalidArgumentException(sprintf('Interval "%s" is not supported.', $interval)),
|
||||
};
|
||||
|
||||
match ($period) {
|
||||
'previous' => $this->dashboardPage->choosePreviousPeriod(),
|
||||
'next' => $this->dashboardPage->chooseNextPeriod(),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I choose :channelName channel
|
||||
*/
|
||||
public function iChooseChannel($channelName)
|
||||
public function iChooseChannel(string $channelName): void
|
||||
{
|
||||
$this->dashboardPage->chooseChannel($channelName);
|
||||
}
|
||||
|
|
@ -64,23 +92,23 @@ final class DashboardContext implements Context
|
|||
/**
|
||||
* @Then I should see :number new orders
|
||||
*/
|
||||
public function iShouldSeeNewOrders($number)
|
||||
public function iShouldSeeNewOrders(int $number): void
|
||||
{
|
||||
Assert::same($this->dashboardPage->getNumberOfNewOrders(), (int) $number);
|
||||
Assert::same($this->dashboardPage->getNumberOfNewOrders(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see :number new customers
|
||||
*/
|
||||
public function iShouldSeeNewCustomers($number)
|
||||
public function iShouldSeeNewCustomers(int $number): void
|
||||
{
|
||||
Assert::same($this->dashboardPage->getNumberOfNewCustomers(), (int) $number);
|
||||
Assert::same($this->dashboardPage->getNumberOfNewCustomers(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then there should be total sales of :total
|
||||
*/
|
||||
public function thereShouldBeTotalSalesOf($total)
|
||||
public function thereShouldBeTotalSalesOf(string $total): void
|
||||
{
|
||||
Assert::same($this->dashboardPage->getTotalSales(), $total);
|
||||
}
|
||||
|
|
@ -88,25 +116,29 @@ final class DashboardContext implements Context
|
|||
/**
|
||||
* @Then the average order value should be :value
|
||||
*/
|
||||
public function myAverageOrderValueShouldBe($value)
|
||||
public function myAverageOrderValueShouldBe(string $value): void
|
||||
{
|
||||
Assert::same($this->dashboardPage->getAverageOrderValue(), $value);
|
||||
Assert::same(
|
||||
$this->dashboardPage->getAverageOrderValue(),
|
||||
$value,
|
||||
'Expected average order value to be equal to %2$s, but it is %s.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see :number new customers in the list
|
||||
*/
|
||||
public function iShouldSeeNewCustomersInTheList($number)
|
||||
public function iShouldSeeNewCustomersInTheList(int $number): void
|
||||
{
|
||||
Assert::same($this->dashboardPage->getNumberOfNewCustomersInTheList(), (int) $number);
|
||||
Assert::same($this->dashboardPage->getNumberOfNewCustomersInTheList(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see :number new orders in the list
|
||||
*/
|
||||
public function iShouldSeeNewOrdersInTheList($number)
|
||||
public function iShouldSeeNewOrdersInTheList(int $number): void
|
||||
{
|
||||
Assert::same($this->dashboardPage->getNumberOfNewOrdersInTheList(), (int) $number);
|
||||
Assert::same($this->dashboardPage->getNumberOfNewOrdersInTheList(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -888,11 +888,19 @@ final class ManagingOrdersContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Then there should be :count changes in the registry
|
||||
* @Then there should be :count shipping address changes in the registry
|
||||
*/
|
||||
public function thereShouldBeCountChangesInTheRegistry($count)
|
||||
public function thereShouldBeCountShippingAddressChangesInTheRegistry(int $count): void
|
||||
{
|
||||
Assert::same($this->historyPage->countShippingAddressChanges(), (int) $count);
|
||||
Assert::same($this->historyPage->countShippingAddressChanges(), $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then there should be :count billing address changes in the registry
|
||||
*/
|
||||
public function thereShouldBeCountBillingAddressChangesInTheRegistry(int $count): void
|
||||
{
|
||||
Assert::same($this->historyPage->countBillingAddressChanges(), $count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ final class ManagingPaymentsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then I should see order page with details of order :order
|
||||
* @Then I should see the details of order :order
|
||||
*/
|
||||
public function iShouldSeeOrderPageWithDetailsOfOrder(OrderInterface $order): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ use Sylius\Behat\Page\Admin\Crud\IndexPageInterface;
|
|||
use Sylius\Behat\Page\Admin\ProductAttribute\CreatePageInterface;
|
||||
use Sylius\Behat\Page\Admin\ProductAttribute\UpdatePageInterface;
|
||||
use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
|
||||
use Sylius\Behat\Service\SharedSecurityServiceInterface;
|
||||
use Sylius\Component\Core\Model\AdminUserInterface;
|
||||
use Sylius\Component\Product\Model\ProductAttributeInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
|
|
@ -30,7 +28,6 @@ final class ManagingProductAttributesContext implements Context
|
|||
private IndexPageInterface $indexPage,
|
||||
private UpdatePageInterface $updatePage,
|
||||
private CurrentPageResolverInterface $currentPageResolver,
|
||||
private SharedSecurityServiceInterface $sharedSecurityService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -233,25 +230,6 @@ final class ManagingProductAttributesContext implements Context
|
|||
$this->indexPage->open();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^(the administrator) changes (this product attribute)'s value "([^"]*)" to "([^"]*)"$/
|
||||
*/
|
||||
public function theAdministratorChangesThisProductAttributesValueTo(
|
||||
AdminUserInterface $user,
|
||||
ProductAttributeInterface $productAttribute,
|
||||
string $oldValue,
|
||||
string $newValue,
|
||||
): void {
|
||||
$this->sharedSecurityService->performActionAsAdminUser(
|
||||
$user,
|
||||
function () use ($productAttribute, $oldValue, $newValue) {
|
||||
$this->iWantToEditThisAttribute($productAttribute);
|
||||
$this->iChangeItsValueTo($oldValue, $newValue);
|
||||
$this->iSaveMyChanges();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I specify its min length as :min
|
||||
* @When I specify its min entries value as :min
|
||||
|
|
@ -286,24 +264,6 @@ final class ManagingProductAttributesContext implements Context
|
|||
// Intentionally left blank to fulfill context expectation
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^(the administrator) deletes the value "([^"]+)" from (this product attribute)$/
|
||||
*/
|
||||
public function theAdministratorDeletesTheValueFromThisProductAttribute(
|
||||
AdminUserInterface $user,
|
||||
string $value,
|
||||
ProductAttributeInterface $productAttribute,
|
||||
): void {
|
||||
$this->sharedSecurityService->performActionAsAdminUser(
|
||||
$user,
|
||||
function () use ($productAttribute, $value) {
|
||||
$this->iWantToEditThisAttribute($productAttribute);
|
||||
$this->iDeleteValue($value);
|
||||
$this->iSaveMyChanges();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I check (also) the :productAttributeName product attribute
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ final class ManagingProductsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then the first product on the list should have :field :value
|
||||
* @Then the first product on the list within this taxon should have :field :value
|
||||
*/
|
||||
public function theFirstProductOnTheListShouldHave($field, $value)
|
||||
{
|
||||
|
|
@ -366,6 +367,7 @@ final class ManagingProductsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then the last product on the list should have :field :value
|
||||
* @Then the last product on the list within this taxon should have :field :value
|
||||
*/
|
||||
public function theLastProductOnTheListShouldHave($field, $value)
|
||||
{
|
||||
|
|
@ -400,6 +402,17 @@ final class ManagingProductsContext implements Context
|
|||
$this->indexPage->sortBy($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I sort this taxon's products :sortType by :field
|
||||
*/
|
||||
public function iSortThisTaxonsProductsBy(string $sortType, string $field): void
|
||||
{
|
||||
$this->indexPerTaxonPage->sortBy(
|
||||
$field,
|
||||
str_starts_with($sortType, 'de') ? 'desc' : 'asc',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see a single product in the list
|
||||
* @Then I should see :numberOfProducts products in the list
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ final class ManagingShipmentsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then I should see order page with details of order :order
|
||||
* @Then I should see the details of order :order
|
||||
*/
|
||||
public function iShouldSeeOrderPageWithDetailsOfOrder(OrderInterface $order): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -146,6 +146,16 @@ final class ManagingShippingMethodsContext implements Context
|
|||
Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $shipmentMethodName]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the shipping method :shipmentMethodName should not appear in the registry
|
||||
*/
|
||||
public function theShipmentMethodShouldNotAppearInTheRegistry(string $shipmentMethodName): void
|
||||
{
|
||||
$this->iWantToBrowseShippingMethods();
|
||||
|
||||
Assert::false($this->indexPage->isSingleResourceOnPage(['name' => $shipmentMethodName]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^(this shipping method) should still be in the registry$/
|
||||
*/
|
||||
|
|
@ -235,7 +245,7 @@ final class ManagingShippingMethodsContext implements Context
|
|||
/**
|
||||
* @Then I should be notified that code needs to contain only specific symbols
|
||||
*/
|
||||
public function iShouldBeNotifiedThatCodeShouldContain()
|
||||
public function iShouldBeNotifiedThatCodeNeedsToContainOnlySpecificSymbols(): void
|
||||
{
|
||||
$this->assertFieldValidationMessage(
|
||||
'code',
|
||||
|
|
@ -324,8 +334,9 @@ final class ManagingShippingMethodsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then I should be notified that :element has to be selected
|
||||
* @Then I should be notified that the :element is required
|
||||
*/
|
||||
public function iShouldBeNotifiedThatElementHasToBeSelected($element)
|
||||
public function iShouldBeNotifiedThatElementHasToBeSelected(string $element): void
|
||||
{
|
||||
$this->assertFieldValidationMessage($element, sprintf('Please select shipping method %s.', $element));
|
||||
}
|
||||
|
|
@ -484,6 +495,15 @@ final class ManagingShippingMethodsContext implements Context
|
|||
$this->createPage->fillRuleOption('Weight', (string) $weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I add the "Total weight greater than or equal" rule configured with invalid data
|
||||
*/
|
||||
public function iAddTheTotalWeightGreaterThanOrEqualRuleConfiguredWithInvalidData(): void
|
||||
{
|
||||
$this->createPage->addRule('Total weight greater than or equal');
|
||||
$this->createPage->fillRuleOption('Weight', 'invalid data');
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I add the "Total weight less than or equal" rule configured with :weight
|
||||
*/
|
||||
|
|
@ -494,21 +514,21 @@ final class ManagingShippingMethodsContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @When /^I add the "Items total greater than or equal" rule configured with (?:€|£|\$)([^"]+) for ("[^"]+" channel)$/
|
||||
* @When /^I add the "([^"]+)" rule configured with (?:€|£|\$)([^"]+) for ("[^"]+" channel)$/
|
||||
*/
|
||||
public function iAddTheItemsTotalGreaterThanOrEqualRuleConfiguredWith($value, ChannelInterface $channel): void
|
||||
public function iAddTheItemsTotalLessThanOrEqualRuleConfiguredWith(string $rule, mixed $value, ChannelInterface $channel): void
|
||||
{
|
||||
$this->createPage->addRule('Items total greater than or equal');
|
||||
$this->createPage->addRule($rule);
|
||||
$this->createPage->fillRuleOptionForChannel($channel->getCode(), 'Amount', (string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I add the "Items total less than or equal" rule configured with (?:€|£|\$)([^"]+) for ("[^"]+" channel)$/
|
||||
* @When /^I add the "Items total less than or equal" rule configured with invalid data for ("[^"]+" channel)$/
|
||||
*/
|
||||
public function iAddTheItemsTotalLessThanOrEqualRuleConfiguredWith($value, ChannelInterface $channel): void
|
||||
public function iAddTheItemsTotalLessThanOrEqualRuleConfiguredWithInvalidData(ChannelInterface $channel): void
|
||||
{
|
||||
$this->createPage->addRule('Items total less than or equal');
|
||||
$this->createPage->fillRuleOptionForChannel($channel->getCode(), 'Amount', (string) $value);
|
||||
$this->createPage->fillRuleOptionForChannel($channel->getCode(), 'Amount', 'Invalid data');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -532,6 +552,28 @@ final class ManagingShippingMethodsContext implements Context
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the weight rule has an invalid configuration
|
||||
*/
|
||||
public function iShouldBeNotifiedThatTheWeightRuleHasAnInvalidConfiguration(): void
|
||||
{
|
||||
$this->assertFieldValidationMessage('weight', 'Please enter a number.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the amount rule has an invalid configuration in :channel channel
|
||||
*/
|
||||
public function iShouldBeNotifiedThatTheAmountRuleHasAnInvalidConfigurationInChannel(ChannelInterface $channel): void
|
||||
{
|
||||
/** @var CreatePageInterface|UpdatePageInterface $currentPage */
|
||||
$currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]);
|
||||
|
||||
Assert::same(
|
||||
$currentPage->getValidationMessageForRuleAmount($channel->getCode()),
|
||||
'Please enter a valid money amount.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $element
|
||||
* @param string $expectedMessage
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ final class ProductShowPageContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @When I access :product product page
|
||||
* @When I access the :product product
|
||||
*/
|
||||
public function iAccessProductPage(ProductInterface $product): void
|
||||
public function iAccessTheProduct(ProductInterface $product): void
|
||||
{
|
||||
$this->indexPage->showProductPage($product->getName());
|
||||
}
|
||||
|
|
@ -285,9 +285,9 @@ final class ProductShowPageContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Then I should see product taxon is :taxonName
|
||||
* @Then I should see product taxon :taxonName
|
||||
*/
|
||||
public function iShouldSeeProductTaxonIs(string $taxonName): void
|
||||
public function iShouldSeeProductTaxon(string $taxonName): void
|
||||
{
|
||||
Assert::true($this->taxonomyElement->hasProductTaxon($taxonName));
|
||||
}
|
||||
|
|
@ -385,7 +385,15 @@ final class ProductShowPageContext implements Context
|
|||
*/
|
||||
public function iShouldSeeProductAssociationWith(string $association, string $productName): void
|
||||
{
|
||||
Assert::true($this->associationsElement->isProductAssociated($association, $productName));
|
||||
Assert::true($this->associationsElement->isAssociatedWith($association, $productName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see product association type :association
|
||||
*/
|
||||
public function iShouldSeeProductAssociationType(string $association): void
|
||||
{
|
||||
Assert::true($this->associationsElement->hasAssociation($association));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -423,6 +431,14 @@ final class ProductShowPageContext implements Context
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see the :variantName variant
|
||||
*/
|
||||
public function iShouldSeeTheVariant(string $variantName): void
|
||||
{
|
||||
Assert::true($this->variantsElement->hasProductVariant($variantName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see attribute :attribute with value :value in :nameOfLocale locale
|
||||
*/
|
||||
|
|
@ -432,7 +448,7 @@ final class ProductShowPageContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Then I should see non-translatable attribute :attribute with value :value
|
||||
* @Then /^I should see non-translatable attribute "([^"]+)" with value ([^"]+)%$/
|
||||
*/
|
||||
public function iShouldSeeNonTranslatableAttributeWithValue(string $attribute, string $value): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,9 +18,13 @@ use FriendsOfBehat\PageObjectExtension\Element\Element;
|
|||
|
||||
final class AssociationsElement extends Element implements AssociationsElementInterface
|
||||
{
|
||||
public function isProductAssociated(string $associationName, string $productName): bool
|
||||
public function hasAssociation(string $associationName): bool
|
||||
{
|
||||
return [] !== $this->getAssociatedProducts($this->getElement('associations'), $associationName);
|
||||
}
|
||||
|
||||
public function isAssociatedWith(string $associationName, string $productName): bool
|
||||
{
|
||||
/** @var NodeElement $associations */
|
||||
$associations = $this->getElement('associations');
|
||||
|
||||
/** @var NodeElement $product */
|
||||
|
|
|
|||
|
|
@ -15,5 +15,7 @@ namespace Sylius\Behat\Element\Product\ShowPage;
|
|||
|
||||
interface AssociationsElementInterface
|
||||
{
|
||||
public function isProductAssociated(string $associationName, string $productName): bool;
|
||||
public function hasAssociation(string $associationName): bool;
|
||||
|
||||
public function isAssociatedWith(string $associationName, string $productName): bool;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ final class AttributesElement extends Element implements AttributesElementInterf
|
|||
{
|
||||
$attributeValue = $this->getDocument()->find('css', sprintf('.ui.segment[data-tab="non-translatable"] tr:contains("%s") td:nth-child(2)', $attribute))->getText();
|
||||
|
||||
return $attributeValue === $value;
|
||||
return str_contains($attributeValue, $value);
|
||||
}
|
||||
|
||||
protected function getDefinedElements(): array
|
||||
|
|
|
|||
|
|
@ -26,6 +26,20 @@ final class VariantsElement extends Element implements VariantsElementInterface
|
|||
return \count($variants);
|
||||
}
|
||||
|
||||
public function hasProductVariant(string $name): bool
|
||||
{
|
||||
$variantRows = $this->getDocument()->findAll('css', '#variants .variants-accordion__title');
|
||||
|
||||
/** @var NodeElement $variant */
|
||||
foreach ($variantRows as $variant) {
|
||||
if ($variant->find('css', '.content .variant-name')->getText() === $name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasProductVariantWithCodePriceAndCurrentStock(
|
||||
string $name,
|
||||
string $code,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ interface VariantsElementInterface
|
|||
{
|
||||
public function countVariantsOnPage(): int;
|
||||
|
||||
public function hasProductVariant(string $name): bool;
|
||||
|
||||
public function hasProductVariantWithCodePriceAndCurrentStock(
|
||||
string $name,
|
||||
string $code,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Sylius\Behat\Page\Admin;
|
||||
|
||||
use Behat\Mink\Exception\ElementNotFoundException;
|
||||
use Behat\Mink\Session;
|
||||
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage;
|
||||
use Sylius\Behat\Service\Accessor\TableAccessorInterface;
|
||||
|
|
@ -20,84 +21,133 @@ use Symfony\Component\Routing\RouterInterface;
|
|||
|
||||
class DashboardPage extends SymfonyPage implements DashboardPageInterface
|
||||
{
|
||||
/**
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
*
|
||||
* @param array<TKey, TValue>|\ArrayAccess<TKey, TValue> $minkParameters
|
||||
*/
|
||||
public function __construct(
|
||||
Session $session,
|
||||
$minkParameters,
|
||||
array|\ArrayAccess $minkParameters,
|
||||
RouterInterface $router,
|
||||
protected TableAccessorInterface $tableAccessor,
|
||||
) {
|
||||
parent::__construct($session, $minkParameters, $router);
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getTotalSales(): string
|
||||
{
|
||||
return $this->getElement('total_sales')->getText();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getNumberOfNewOrders(): int
|
||||
{
|
||||
return (int) $this->getElement('new_orders')->getText();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getNumberOfNewOrdersInTheList(): int
|
||||
{
|
||||
return $this->tableAccessor->countTableBodyRows($this->getElement('order_list'));
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getNumberOfNewCustomers(): int
|
||||
{
|
||||
return (int) $this->getElement('new_customers')->getText();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getNumberOfNewCustomersInTheList(): int
|
||||
{
|
||||
return $this->tableAccessor->countTableBodyRows($this->getElement('customer_list'));
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getAverageOrderValue(): string
|
||||
{
|
||||
return $this->getElement('average_order_value')->getText();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getSubHeader(): string
|
||||
{
|
||||
return trim($this->getElement('sub_header')->getText());
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function isSectionWithLabelVisible(string $name): bool
|
||||
{
|
||||
return $this->getElement('admin_menu')->find('css', sprintf('div:contains(%s)', $name)) !== null;
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function logOut(): void
|
||||
{
|
||||
$this->getElement('logout')->click();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function chooseChannel(string $channelName): void
|
||||
{
|
||||
$this->getElement('channel_choosing_link', ['%channelName%' => $channelName])->click();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function chooseYearSplitByMonthsInterval(): void
|
||||
{
|
||||
$this->getElement('year_split_by_months_statistics_button')->click();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function chooseMonthSplitByDaysInterval(): void
|
||||
{
|
||||
$this->getElement('month_split_by_days_statistics_button')->click();
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function choosePreviousPeriod(): void
|
||||
{
|
||||
$this->getElement('navigation_previous')->click();
|
||||
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function chooseNextPeriod(): void
|
||||
{
|
||||
$this->getElement('navigation_next')->click();
|
||||
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
public function getRouteName(): string
|
||||
{
|
||||
return 'sylius_admin_dashboard';
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function getDefinedElements(): array
|
||||
{
|
||||
return array_merge(parent::getDefinedElements(), [
|
||||
'admin_menu' => '.sylius-admin-menu',
|
||||
'average_order_value' => '#average-order-value',
|
||||
'channel_choosing_link' => 'a:contains("%channelName%")',
|
||||
'customer_list' => '#customers',
|
||||
'dropdown' => 'i.dropdown',
|
||||
'logout' => '#sylius-logout-button',
|
||||
'month_split_by_days_statistics_button' => 'button[data-stats-button="month"]',
|
||||
'navigation_next' => '#navigation-next',
|
||||
'navigation_previous' => '#navigation-prev',
|
||||
'new_customers' => '#new-customers',
|
||||
'new_orders' => '#new-orders',
|
||||
'order_list' => '#orders',
|
||||
'total_sales' => '#total-sales',
|
||||
'sub_header' => '.ui.header .content .sub.header',
|
||||
'channel_choosing_link' => 'a:contains("%channelName%")',
|
||||
'admin_menu' => '.sylius-admin-menu',
|
||||
'total_sales' => '#total-sales',
|
||||
'year_split_by_months_statistics_button' => 'button[data-stats-button="year"]',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,12 @@ interface DashboardPageInterface extends SymfonyPageInterface
|
|||
public function logOut(): void;
|
||||
|
||||
public function chooseChannel(string $channelName): void;
|
||||
|
||||
public function chooseYearSplitByMonthsInterval(): void;
|
||||
|
||||
public function chooseMonthSplitByDaysInterval(): void;
|
||||
|
||||
public function choosePreviousPeriod(): void;
|
||||
|
||||
public function chooseNextPeriod(): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,4 +26,9 @@ class HistoryPage extends SymfonyPage implements HistoryPageInterface
|
|||
{
|
||||
return count($this->getDocument()->findAll('css', '#shipping-address-changes tbody tr'));
|
||||
}
|
||||
|
||||
public function countBillingAddressChanges(): int
|
||||
{
|
||||
return count($this->getDocument()->findAll('css', '#billing-address-changes tbody tr'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ use FriendsOfBehat\PageObjectExtension\Page\SymfonyPageInterface;
|
|||
interface HistoryPageInterface extends SymfonyPageInterface
|
||||
{
|
||||
public function countShippingAddressChanges(): int;
|
||||
|
||||
public function countBillingAddressChanges(): int;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,21 +75,15 @@ class CreatePage extends BaseCreatePage implements CreatePageInterface
|
|||
public function getValidationMessageForAmount(string $channelCode): string
|
||||
{
|
||||
$foundElement = $this->getFieldElement('amount', ['%channelCode%' => $channelCode]);
|
||||
if (null === $foundElement) {
|
||||
throw new ElementNotFoundException($this->getSession(), 'Field element');
|
||||
}
|
||||
|
||||
$validationMessage = $foundElement->find('css', '.sylius-validation-error');
|
||||
if (null === $validationMessage) {
|
||||
throw new ElementNotFoundException(
|
||||
$this->getSession(),
|
||||
'Validation message',
|
||||
'css',
|
||||
'.sylius-validation-error',
|
||||
);
|
||||
}
|
||||
return $this->getValidationMessageForElement($foundElement);
|
||||
}
|
||||
|
||||
return $validationMessage->getText();
|
||||
public function getValidationMessageForRuleAmount(string $channelCode): string
|
||||
{
|
||||
$foundElement = $this->getChannelConfigurationOfLastRule($channelCode);
|
||||
|
||||
return $this->getValidationMessageForElement($foundElement);
|
||||
}
|
||||
|
||||
public function addRule(string $ruleName): void
|
||||
|
|
@ -126,6 +120,7 @@ class CreatePage extends BaseCreatePage implements CreatePageInterface
|
|||
'channel' => '#sylius_shipping_method_channels .ui.checkbox:contains("%channel%")',
|
||||
'calculator' => '#sylius_shipping_method_calculator',
|
||||
'calculator_configuration' => '.ui.segment.configuration',
|
||||
'weight' => '[id*="sylius_shipping_method_rules_"][id*="_configuration_weight"]',
|
||||
'code' => '#sylius_shipping_method_code',
|
||||
'name' => '#sylius_shipping_method_translations_en_US_name',
|
||||
'zone' => '#sylius_shipping_method_zone',
|
||||
|
|
@ -133,6 +128,25 @@ class CreatePage extends BaseCreatePage implements CreatePageInterface
|
|||
]);
|
||||
}
|
||||
|
||||
private function getValidationMessageForElement(NodeElement $element): string
|
||||
{
|
||||
if (null === $element) {
|
||||
throw new ElementNotFoundException($this->getSession(), 'Field element');
|
||||
}
|
||||
|
||||
$validationMessage = $element->find('css', '.sylius-validation-error');
|
||||
if (null === $validationMessage) {
|
||||
throw new ElementNotFoundException(
|
||||
$this->getSession(),
|
||||
'Validation message',
|
||||
'css',
|
||||
'.sylius-validation-error',
|
||||
);
|
||||
}
|
||||
|
||||
return $validationMessage->getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ElementNotFoundException
|
||||
*/
|
||||
|
|
@ -167,7 +181,7 @@ class CreatePage extends BaseCreatePage implements CreatePageInterface
|
|||
return $items;
|
||||
}
|
||||
|
||||
private function getChannelConfigurationOfLastRule(string $channelCode): NodeElement
|
||||
private function getChannelConfigurationOfLastRule(string $channelCode): ?NodeElement
|
||||
{
|
||||
return $this
|
||||
->getLastCollectionItem('rules')
|
||||
|
|
|
|||
|
|
@ -32,13 +32,14 @@ interface CreatePageInterface extends BaseCreatePageInterface
|
|||
|
||||
public function chooseCalculator(string $name): void;
|
||||
|
||||
public function checkChannel($channelName): void;
|
||||
public function checkChannel(string $channelName): void;
|
||||
|
||||
/**
|
||||
* @throws ElementNotFoundException
|
||||
*/
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getValidationMessageForAmount(string $channelCode): string;
|
||||
|
||||
/** @throws ElementNotFoundException */
|
||||
public function getValidationMessageForRuleAmount(string $channelCode): string;
|
||||
|
||||
public function addRule(string $ruleName): void;
|
||||
|
||||
public function selectRuleOption(string $option, string $value, bool $multiple = false): void;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@
|
|||
<service id="Sylius\Behat\Context\Api\Admin\ManagingProductTaxonsContext">
|
||||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="api_platform.iri_converter" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
<argument type="service" id="sylius.behat.shared_storage" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.api.admin.managing_channels" class="Sylius\Behat\Context\Api\Admin\ManagingChannelsContext">
|
||||
|
|
@ -101,6 +103,15 @@
|
|||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.api.admin.managing_product_associations" class="Sylius\Behat\Context\Api\Admin\ManagingProductAssociationsContext">
|
||||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
<argument type="service" id="api_platform.iri_converter" />
|
||||
<argument type="service" id="sylius.behat.section_iri_converter" />
|
||||
<argument type="service" id="sylius.repository.product_association" />
|
||||
<argument type="service" id="sylius.behat.shared_storage" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.api.admin.managing_product_association_types" class="Sylius\Behat\Context\Api\Admin\ManagingProductAssociationTypesContext">
|
||||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
|
|
@ -195,6 +206,7 @@
|
|||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
<argument type="service" id="sylius.behat.section_iri_converter" />
|
||||
<argument type="service" id="api_platform.iri_converter" />
|
||||
<argument>%sylius.security.new_api_route%</argument>
|
||||
</service>
|
||||
|
||||
|
|
@ -210,10 +222,12 @@
|
|||
<service id="sylius.behat.context.api.admin.managing_orders" class="Sylius\Behat\Context\Api\Admin\ManagingOrdersContext">
|
||||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
<argument type="service" id="sylius.behat.request_factory" />
|
||||
<argument type="service" id="api_platform.iri_converter" />
|
||||
<argument type="service" id="sylius.behat.api_security" />
|
||||
<argument type="service" id="sylius.behat.shared_storage" />
|
||||
<argument type="service" id="sylius.behat.api.shared_security" />
|
||||
<argument type="service" id="sylius.behat.section_iri_converter" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Behat\Context\Api\Admin\ManagingPaymentMethodsContext">
|
||||
|
|
@ -291,7 +305,7 @@
|
|||
<argument type="service" id="api_platform.iri_converter" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Behat\Context\Api\Admin\ManagingPlacedOrderAddressesContext">
|
||||
<service id="sylius.behat.context.api.admin.managing_placed_order_addresses" class="Sylius\Behat\Context\Api\Admin\ManagingPlacedOrderAddressesContext">
|
||||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
<argument type="service" id="sylius.behat.shared_storage" />
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@
|
|||
<argument type="service" id="sylius.behat.factory.default_channel" />
|
||||
<argument type="service" id="sylius.repository.channel" />
|
||||
<argument type="service" id="sylius.manager.channel" />
|
||||
<argument type="service" id="sylius.repository.locale" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.setup.currency" class="Sylius\Behat\Context\Setup\CurrencyContext">
|
||||
|
|
@ -124,6 +123,7 @@
|
|||
<argument type="service" id="Sylius\Component\Product\Resolver\ProductVariantResolverInterface" />
|
||||
<argument type="service" id="sylius.order_item_quantity_modifier" />
|
||||
<argument type="service" id="doctrine.orm.entity_manager" />
|
||||
<argument type="service" id="sylius.behat.clock" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.setup.payment" class="Sylius\Behat\Context\Setup\PaymentContext">
|
||||
|
|
|
|||
|
|
@ -207,7 +207,6 @@
|
|||
<argument type="service" id="sylius.behat.page.admin.product_attribute.index" />
|
||||
<argument type="service" id="sylius.behat.page.admin.product_attribute.update" />
|
||||
<argument type="service" id="sylius.behat.current_page_resolver" />
|
||||
<argument type="service" id="sylius.behat.shared_security" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.ui.admin.managing_product_options" class="Sylius\Behat\Context\Ui\Admin\ManagingProductOptionsContext">
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ imports:
|
|||
- suites/api/locale/managing_locales.yml
|
||||
- suites/api/order/managing_orders.yml
|
||||
- suites/api/order/modifying_placed_order_address.yaml
|
||||
- suites/api/order/order_history.yaml
|
||||
- suites/api/payment/managing_payment_methods.yaml
|
||||
- suites/api/payment/managing_payments.yml
|
||||
- suites/api/product/adding_product_review.yml
|
||||
|
|
@ -90,7 +91,7 @@ imports:
|
|||
- suites/ui/account/login.yml
|
||||
- suites/ui/addressing/managing_countries.yml
|
||||
- suites/ui/addressing/managing_zones.yml
|
||||
- suites/ui/admin/dashboard.yml
|
||||
- suites/ui/admin/dashboard.yaml
|
||||
- suites/ui/admin/impersonating_customers.yml
|
||||
- suites/ui/admin/locale.yml
|
||||
- suites/ui/admin/login.yml
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ default:
|
|||
- sylius.behat.context.setup.currency
|
||||
- sylius.behat.context.setup.customer
|
||||
- sylius.behat.context.setup.geographical
|
||||
- sylius.behat.context.setup.locale
|
||||
- sylius.behat.context.setup.order
|
||||
- sylius.behat.context.setup.payment
|
||||
- sylius.behat.context.setup.product
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ default:
|
|||
- sylius.behat.context.transform.shared_storage
|
||||
|
||||
- sylius.behat.context.api.admin.managing_orders
|
||||
- sylius.behat.context.api.admin.managing_placed_order_addresses
|
||||
- sylius.behat.context.api.admin.response
|
||||
- sylius.behat.context.api.admin.save
|
||||
- Sylius\Behat\Context\Api\Admin\ManagingPlacedOrderAddressesContext
|
||||
|
||||
filters:
|
||||
tags: "@modifying_placed_order_address&&@api"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
# This file is part of the Sylius package.
|
||||
# (c) Sylius Sp. z o.o.
|
||||
|
||||
default:
|
||||
suites:
|
||||
api_order_history:
|
||||
contexts:
|
||||
- sylius.behat.context.hook.doctrine_orm
|
||||
- sylius.behat.context.hook.session
|
||||
|
||||
- sylius.behat.context.setup.admin_api_security
|
||||
- sylius.behat.context.setup.channel
|
||||
- sylius.behat.context.setup.order
|
||||
- sylius.behat.context.setup.payment
|
||||
- sylius.behat.context.setup.product
|
||||
- sylius.behat.context.setup.shipping
|
||||
|
||||
- sylius.behat.context.transform.address
|
||||
- sylius.behat.context.transform.channel
|
||||
- sylius.behat.context.transform.country
|
||||
- sylius.behat.context.transform.customer
|
||||
- sylius.behat.context.transform.lexical
|
||||
- sylius.behat.context.transform.order
|
||||
- sylius.behat.context.transform.payment
|
||||
- sylius.behat.context.transform.product
|
||||
- sylius.behat.context.transform.shipping_method
|
||||
- sylius.behat.context.transform.zone
|
||||
|
||||
- sylius.behat.context.transform.shared_storage
|
||||
|
||||
- sylius.behat.context.api.admin.managing_orders
|
||||
- sylius.behat.context.api.admin.managing_placed_order_addresses
|
||||
- sylius.behat.context.api.admin.response
|
||||
- sylius.behat.context.api.admin.save
|
||||
|
||||
filters:
|
||||
tags: "@order_history&&@api"
|
||||
|
|
@ -44,6 +44,7 @@ default:
|
|||
- sylius.behat.context.setup.taxonomy
|
||||
- sylius.behat.context.setup.zone
|
||||
|
||||
- sylius.behat.context.api.admin.managing_product_associations
|
||||
- sylius.behat.context.api.admin.managing_product_images
|
||||
- sylius.behat.context.api.admin.managing_products
|
||||
- sylius.behat.context.api.admin.response
|
||||
|
|
|
|||
|
|
@ -8,20 +8,38 @@ default:
|
|||
- sylius.behat.context.hook.doctrine_orm
|
||||
|
||||
- sylius.behat.context.transform.channel
|
||||
- sylius.behat.context.transform.currency
|
||||
- sylius.behat.context.transform.lexical
|
||||
- sylius.behat.context.transform.locale
|
||||
- sylius.behat.context.transform.product
|
||||
- sylius.behat.context.transform.product_association_type
|
||||
- sylius.behat.context.transform.product_attribute
|
||||
- sylius.behat.context.transform.product_option
|
||||
- sylius.behat.context.transform.product_option_value
|
||||
- sylius.behat.context.transform.product_variant
|
||||
- sylius.behat.context.transform.shared_storage
|
||||
- sylius.behat.context.transform.shipping_category
|
||||
- sylius.behat.context.transform.tax_category
|
||||
- sylius.behat.context.transform.taxon
|
||||
- Sylius\Behat\Context\Transform\CatalogPromotionContext
|
||||
|
||||
- sylius.behat.context.setup.admin_api_security
|
||||
- sylius.behat.context.setup.calendar
|
||||
- sylius.behat.context.setup.channel
|
||||
- sylius.behat.context.setup.locale
|
||||
- sylius.behat.context.setup.product
|
||||
- sylius.behat.context.setup.product_association
|
||||
- sylius.behat.context.setup.product_attribute
|
||||
- sylius.behat.context.setup.product_option
|
||||
- sylius.behat.context.setup.product_taxon
|
||||
- sylius.behat.context.setup.shipping_category
|
||||
- sylius.behat.context.setup.taxation
|
||||
- sylius.behat.context.setup.taxonomy
|
||||
- Sylius\Behat\Context\Setup\CatalogPromotionContext
|
||||
- Sylius\Behat\Context\Setup\PriceHistoryContext
|
||||
|
||||
- sylius.behat.context.api.admin.managing_products
|
||||
- Sylius\Behat\Context\Api\Admin\BrowsingCatalogPromotionProductVariantsContext
|
||||
|
||||
filters:
|
||||
tags: "@viewing_product_in_admin_panel&&@api"
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ default:
|
|||
- sylius.behat.context.transform.product_variant
|
||||
- sylius.behat.context.transform.shared_storage
|
||||
- sylius.behat.context.transform.taxon
|
||||
- Sylius\Behat\Context\Transform\CatalogPromotionContext
|
||||
|
||||
- sylius.behat.context.setup.admin_user
|
||||
- sylius.behat.context.setup.admin_security
|
||||
- sylius.behat.context.setup.channel
|
||||
- sylius.behat.context.setup.customer
|
||||
- sylius.behat.context.setup.locale
|
||||
|
|
@ -27,7 +27,7 @@ default:
|
|||
- sylius.behat.context.setup.product_attribute
|
||||
- sylius.behat.context.setup.product_review
|
||||
- sylius.behat.context.setup.product_taxon
|
||||
- sylius.behat.context.setup.shop_security
|
||||
- sylius.behat.context.setup.shop_api_security
|
||||
- sylius.behat.context.setup.taxonomy
|
||||
- Sylius\Behat\Context\Setup\CatalogPromotionContext
|
||||
- Sylius\Behat\Context\Setup\PriceHistoryContext
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ default:
|
|||
- sylius.behat.context.hook.doctrine_orm
|
||||
- sylius.behat.context.hook.session
|
||||
|
||||
- sylius.behat.context.setup.calendar
|
||||
- sylius.behat.context.setup.channel
|
||||
- sylius.behat.context.setup.currency
|
||||
- sylius.behat.context.setup.geographical
|
||||
|
|
@ -19,6 +20,7 @@ default:
|
|||
- sylius.behat.context.setup.shipping
|
||||
- sylius.behat.context.setup.zone
|
||||
|
||||
- sylius.behat.context.transform.channel
|
||||
- sylius.behat.context.transform.customer
|
||||
- sylius.behat.context.transform.lexical
|
||||
- sylius.behat.context.transform.order
|
||||
|
|
@ -9,7 +9,9 @@ default:
|
|||
- sylius.behat.context.hook.session
|
||||
|
||||
- sylius.behat.context.transform.channel
|
||||
- sylius.behat.context.transform.currency
|
||||
- sylius.behat.context.transform.lexical
|
||||
- sylius.behat.context.transform.locale
|
||||
- sylius.behat.context.transform.product
|
||||
- sylius.behat.context.transform.product_association_type
|
||||
- sylius.behat.context.transform.product_option
|
||||
|
|
@ -19,19 +21,26 @@ default:
|
|||
- sylius.behat.context.transform.shipping_category
|
||||
- sylius.behat.context.transform.tax_category
|
||||
- sylius.behat.context.transform.taxon
|
||||
- Sylius\Behat\Context\Transform\CatalogPromotionContext
|
||||
|
||||
- sylius.behat.context.setup.admin_security
|
||||
- sylius.behat.context.setup.calendar
|
||||
- sylius.behat.context.setup.channel
|
||||
- sylius.behat.context.setup.locale
|
||||
- sylius.behat.context.setup.product
|
||||
- sylius.behat.context.setup.product_association
|
||||
- sylius.behat.context.setup.product_attribute
|
||||
- sylius.behat.context.setup.product_option
|
||||
- sylius.behat.context.setup.product_taxon
|
||||
- sylius.behat.context.setup.shipping_category
|
||||
- sylius.behat.context.setup.taxonomy
|
||||
- sylius.behat.context.setup.taxation
|
||||
- sylius.behat.context.setup.taxonomy
|
||||
- Sylius\Behat\Context\Setup\CatalogPromotionContext
|
||||
- Sylius\Behat\Context\Setup\PriceHistoryContext
|
||||
|
||||
- sylius.behat.context.ui.admin.managing_product_attributes
|
||||
- sylius.behat.context.ui.admin.product_showpage
|
||||
- sylius.behat.context.ui.shop.browsing_product
|
||||
|
||||
filters:
|
||||
tags: "@viewing_product_in_admin_panel&&@ui"
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@ default:
|
|||
- Sylius\Behat\Context\Setup\PriceHistoryContext
|
||||
- sylius.behat.context.setup.calendar
|
||||
|
||||
- sylius.behat.context.ui.admin.managing_product_attributes
|
||||
- sylius.behat.context.ui.admin.product_showpage
|
||||
- sylius.behat.context.ui.channel
|
||||
- sylius.behat.context.ui.shop.locale
|
||||
- sylius.behat.context.ui.shop.product
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ final class MainMenuBuilder
|
|||
{
|
||||
$configuration = $menu
|
||||
->addChild('official_support')
|
||||
->setLabel('sylius.menu.admin.main.official_support.header' )
|
||||
->setLabel('sylius.menu.admin.main.official_support.header')
|
||||
;
|
||||
|
||||
$configuration
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<?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\ApiBundle\Controller;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Sylius\Bundle\ApiBundle\Query\GetAddressLogEntryCollection;
|
||||
use Sylius\Component\Addressing\Model\AddressLogEntry;
|
||||
use Symfony\Component\Messenger\HandleTrait;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
/** @experimental */
|
||||
final class GetAddressLogEntryCollectionAction
|
||||
{
|
||||
use HandleTrait;
|
||||
|
||||
public function __construct(MessageBusInterface $messageBus)
|
||||
{
|
||||
$this->messageBus = $messageBus;
|
||||
}
|
||||
|
||||
/** @return Collection<array-key, AddressLogEntry> */
|
||||
public function __invoke(int $id): Collection
|
||||
{
|
||||
return $this->handle(
|
||||
$this->messageBus->dispatch(
|
||||
new GetAddressLogEntryCollection($id),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,11 @@ final class Configuration implements ConfigurationInterface
|
|||
->defaultFalse()
|
||||
->end()
|
||||
->end()
|
||||
->children()
|
||||
->arrayNode('order_states_to_filter_out')
|
||||
->scalarPrototype()->end()
|
||||
->end()
|
||||
->end()
|
||||
->children()
|
||||
->variableNode('product_image_prefix')
|
||||
->defaultValue('media/image')
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ final class SyliusApiExtension extends Extension implements PrependExtensionInte
|
|||
'sylius_api.filter_eager_loading_extension.restricted_resources',
|
||||
$config['filter_eager_loading_extension']['restricted_resources'],
|
||||
);
|
||||
$container->setParameter('sylius_api.order_states_to_filter_out', $config['order_states_to_filter_out']);
|
||||
|
||||
$loader->load('services.xml');
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
<?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\ApiBundle\Doctrine\QueryExtension;
|
||||
|
||||
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\ContextAwareQueryCollectionExtensionInterface;
|
||||
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
|
||||
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||
use Doctrine\DBAL\ArrayParameterType;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Sylius\Bundle\ApiBundle\SectionResolver\AdminApiSection;
|
||||
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
|
||||
/** @experimental */
|
||||
final class OrderExtension implements ContextAwareQueryCollectionExtensionInterface, QueryItemExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @param array<string> $orderStatesToFilterOut
|
||||
*/
|
||||
public function __construct(
|
||||
private SectionProviderInterface $sectionProvider,
|
||||
private array $orderStatesToFilterOut,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $context
|
||||
*/
|
||||
public function applyToCollection(
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
string $resourceClass,
|
||||
string $operationName = null,
|
||||
array $context = [],
|
||||
): void {
|
||||
$this->filterOutOrders($queryBuilder, $queryNameGenerator, $resourceClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $context
|
||||
* @param array<mixed> $identifiers
|
||||
*/
|
||||
public function applyToItem(
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
string $resourceClass,
|
||||
array $identifiers,
|
||||
string $operationName = null,
|
||||
array $context = [],
|
||||
): void {
|
||||
$this->filterOutOrders($queryBuilder, $queryNameGenerator, $resourceClass);
|
||||
}
|
||||
|
||||
private function filterOutOrders(
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
string $resourceClass,
|
||||
): void {
|
||||
if (!is_a($resourceClass, OrderInterface::class, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->sectionProvider->getSection() instanceof AdminApiSection) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stateParameter = $queryNameGenerator->generateParameterName('state');
|
||||
$rootAlias = $queryBuilder->getRootAliases()[0];
|
||||
|
||||
$queryBuilder
|
||||
->andWhere($queryBuilder->expr()->notIn(sprintf('%s.state', $rootAlias), sprintf(':%s', $stateParameter)))
|
||||
->setParameter($stateParameter, $this->orderStatesToFilterOut, ArrayParameterType::STRING)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
|
||||
/** @experimental */
|
||||
final class CatalogPromotionChannelFilter extends AbstractContextAwareFilter
|
||||
final class ChannelsAwareChannelFilter extends AbstractContextAwareFilter
|
||||
{
|
||||
public function __construct(
|
||||
private IriConverterInterface $iriConverter,
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue