mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
bug #13676 [Cart] Fix retrieving/overriding cart of logged in user by guest (SirDomin, GSadee)
This PR was merged into the 1.10 branch. Discussion ---------- | Q | A | --------------- | ----- | Branch? | 1.10 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Related tickets | replaces https://github.com/Sylius/Sylius/pull/13603 and https://github.com/Sylius/Sylius/pull/13634 | License | MIT <!-- - Bug fixes must be submitted against the 1.10 or 1.11 branch(the lowest possible) - Features and deprecations must be submitted against the master branch - Make sure that the correct base branch is set To be sure you are not breaking any Backward Compatibilities, check the documentation: https://docs.sylius.com/en/latest/book/organization/backward-compatibility-promise.html --> Commits -------570fe09de1[Behat][UI] Prevent anonymous user to claim logged user cartf1c7530780[Behat] Add session manager service8d588731f9[Cart] Return first cart for customer from repository665738674c[Core] Mark cart as placed by gueste0c7302dffAdd info to UPGRADE filefcd58556cd[Behat] Add additional scenarios to cover claiming cart by a proper user after login/registration2ff78cfed4[Cart] Revert change for returning first cart for customer from repositorye1fa6f6ced[Order] Rename field guest to byGuestb0401a314a[Cart] Improve setting byGuest flag in ShopBasedCartContext0d65e9a998[Cart] Set byGuest flag during cart blamingb9c36f7f82[ADR] Describe the solution of retrieving cart problem314cd103e6[Migrations] Add missing mysql check for migrations7f9154b1c9[Cart] Improvements to fixing problem with cart after PR reviewdad642ac91[Cart] Extract resolving byGuest flag to a separate servicecdd57b196f[ADR] Improve the solution of overriding cart problemeb0a597746[Order] Rename field byGuest to createdByGuest
This commit is contained in:
commit
0a77a6d6af
28 changed files with 770 additions and 9 deletions
|
|
@ -1,3 +1,11 @@
|
|||
# UPGRADE FROM `v1.10.8` TO `v1.10.10`
|
||||
|
||||
1. Field `createdByGuest` has been added to `Sylius\Component\Core\Model\Order`, this change will allow us to distinguish carts
|
||||
between guests and logged in customers.
|
||||
|
||||
2. Not passing `createdByGuestFlagResolver` through constructor in `Sylius\Component\Core\Cart\Context\ShopBasedCartContext`
|
||||
is deprecated in Sylius 1.10.9 and it will be prohibited in Sylius 2.0.
|
||||
|
||||
# UPGRADE FROM `v1.10.x` TO `v1.10.8`
|
||||
|
||||
1. Update `payum/payum` to `^1.7` and execute Doctrine Migrations
|
||||
|
|
|
|||
60
adr/2022_02_21_using_cart_by_guest_and_logged_in_customer.md
Normal file
60
adr/2022_02_21_using_cart_by_guest_and_logged_in_customer.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Using cart by guest and logged in customer
|
||||
|
||||
* Status: accepted
|
||||
* Date: 2022-02-22
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Cart and its processing is one of the key aspects of Sylius. It turned out that it has a vulnerability and there is
|
||||
possible for an anonymous user to override the cart of logged in customer by using only its email. This is because
|
||||
when entering an email, during addressing step, the customer with this email is assigned to the cart and from then,
|
||||
there is no simple way to distinguish between the carts created by the guest and the logged in user. The question is
|
||||
how should we distinguish the carts to solve the vulnerability.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
Provided solution should:
|
||||
* solve the initial problem with overriding cart of logged in customer by anonymous user
|
||||
* not break a backward compatibility, both code and business behaviour
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Forcing logging in during checkout
|
||||
|
||||
* Good, because it solves the initial problem
|
||||
* Bad, because it changes the current expected behaviour in checkout
|
||||
* Bad, because it breaks a backward compatibility from the business perspective
|
||||
|
||||
### Changing priority of cart contexts
|
||||
|
||||
* Good, because it solves the initial problem
|
||||
* Bad, because it does not solve the case when a logged in customer does not have a cart
|
||||
* Bad, because it changes the current expected behaviour with keeping cart after logging in
|
||||
* Bad, because it breaks a backward compatibility from the business perspective
|
||||
|
||||
### Introducing flag on order entity to mark order created by guest
|
||||
|
||||
Adding a flag to order entity allows us to distinguish carts created by guest or by logged in customer. To mark cart
|
||||
as soon as possible, to solve all cases of the initial problem, the flag needs to be set in Sylius\Component\Core\Cart\Context\ShopBasedCartContext,
|
||||
where the customer of logged in user is also set on cart. This context is definitely not a proper service to do such operations,
|
||||
however it is consistent with current approach in Sylius. Setting this value only after logging in would not be enough,
|
||||
as it doesn't resolve the situation when the already logged in user creates a cart. It is similar with setting flag after
|
||||
the addressing step, it doesn't resolve the problem as the cart of logged in user is marked too late and before addressing
|
||||
the cart couldn't be distinguished to whom it belongs.
|
||||
|
||||
* Good, because it solves the initial problem
|
||||
* Good, because it does not break a backward compatibility
|
||||
* Good, because it can be used to additional features in the future
|
||||
* Bad, because it requires to set the flag in Sylius\Component\Core\Cart\Context\ShopBasedCartContext
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: **"Introducing flag on order entity to mark order created by guest"**
|
||||
|
||||
For now, it is the most straightforward solution, that resolves the initial problem, does not introduce any BC break
|
||||
and it is the solution that could be used to additional features in the future.
|
||||
|
||||
## References
|
||||
|
||||
* [Approach with changing priorities](https://github.com/Sylius/Sylius/pull/13603)
|
||||
* [Chosen approach with introducing flag](https://github.com/Sylius/Sylius/pull/13676)
|
||||
|
|
@ -33,6 +33,10 @@ default:
|
|||
chrome:
|
||||
api_url: http://127.0.0.1:9222
|
||||
validate_certificate: false
|
||||
chrome_headless_second_session:
|
||||
chrome:
|
||||
api_url: http://127.0.0.1:9222
|
||||
validate_certificate: false
|
||||
chrome:
|
||||
selenium2:
|
||||
browser: chrome
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
@checkout
|
||||
Feature: Preventing from claiming cart of a wrong user
|
||||
In order to make the checkout cart available only for user who owns the cart
|
||||
As a Customer
|
||||
I want to be able to checkout with my previous cart when someone used my email in checkout
|
||||
|
||||
Background:
|
||||
Given the store operates on a single channel in "United States"
|
||||
And the store has a product "PHP T-Shirt" priced at "$20.00"
|
||||
And the store has a product "Kotlin T-Shirt" priced at "$30.00"
|
||||
And the store has a product "Symfony T-Shirt" priced at "$100.00"
|
||||
And the store has a product "Sylius T-Shirt" priced at "$150.00"
|
||||
And the store ships everywhere for free
|
||||
And the store allows paying offline
|
||||
And there is a user "robb@stark.com" identified by "KingInTheNorth"
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Preventing anonymous user from claiming cart of logged in user
|
||||
Given I am logged in as "robb@stark.com"
|
||||
And I have product "PHP T-Shirt" in the cart
|
||||
When an anonymous user in another browser adds products "PHP T-Shirt" and "Kotlin T-Shirt" to the cart
|
||||
And they complete addressing step with email "robb@stark.com" and "United States" based billing address
|
||||
And they add product "Symfony T-Shirt" to the cart
|
||||
Then their cart total should be "$150.00"
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Preventing anonymous user from claiming cart of logged in user
|
||||
Given I am logged in as "robb@stark.com"
|
||||
And I have product "PHP T-Shirt" in the cart
|
||||
When an anonymous user in another browser adds products "PHP T-Shirt" and "Kotlin T-Shirt" to the cart
|
||||
And they complete addressing step with email "robb@stark.com" and "United States" based billing address
|
||||
And they add product "Symfony T-Shirt" to the cart
|
||||
And I view my cart in the previous session
|
||||
Then there should be one item in my cart
|
||||
And my cart's total should be "$20.00"
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Preventing anonymous user from claiming cart of logged in user
|
||||
Given I have product "PHP T-Shirt" in the cart
|
||||
When I sign in with email "robb@stark.com" and password "KingInTheNorth"
|
||||
And I log out
|
||||
And an anonymous user in another browser adds products "PHP T-Shirt" and "Kotlin T-Shirt" to the cart
|
||||
And they complete addressing step with email "robb@stark.com" and "United States" based billing address
|
||||
And they add product "Symfony T-Shirt" to the cart
|
||||
And I sign in again with email "robb@stark.com" and password "KingInTheNorth" in the previous session
|
||||
And I see the summary of my previous cart
|
||||
Then there should be one item in my cart
|
||||
And my cart's total should be "$20.00"
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Preventing anonymous user from claiming cart of logged in user
|
||||
Given on this channel account verification is not required
|
||||
And I have product "PHP T-Shirt" in the cart
|
||||
When I register with email "eddard@stark.com" and password "handOfTheKing"
|
||||
And I log out
|
||||
And an anonymous user in another browser adds products "PHP T-Shirt" and "Kotlin T-Shirt" to the cart
|
||||
And they complete addressing step with email "robb@stark.com" and "United States" based billing address
|
||||
And they add product "Symfony T-Shirt" to the cart
|
||||
And I sign in again with email "eddard@stark.com" and password "handOfTheKing" in the previous session
|
||||
And I see the summary of my previous cart
|
||||
Then there should be one item in my cart
|
||||
And my cart's total should be "$20.00"
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Preventing logged in user from claiming cart of anonymous user
|
||||
Given an anonymous user added product "Kotlin T-Shirt" to the cart
|
||||
And they have completed addressing step with email "robb@stark.com" and "United States" based billing address
|
||||
When I log in as "robb@stark.com"
|
||||
And I add product "Sylius T-Shirt" to the cart
|
||||
And I view my cart in the previous session
|
||||
Then there should be one item in my cart
|
||||
And my cart's total should be "$150.00"
|
||||
|
|
@ -44,6 +44,7 @@ final class ShopSecurityContext implements Context
|
|||
|
||||
/**
|
||||
* @Given I am logged in as :email
|
||||
* @When I log in as :email
|
||||
*/
|
||||
public function iAmLoggedInAs(string $email): void
|
||||
{
|
||||
|
|
|
|||
64
src/Sylius/Behat/Context/Ui/Shop/AuthorizationContext.php
Normal file
64
src/Sylius/Behat/Context/Ui/Shop/AuthorizationContext.php
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Behat\Context\Ui\Shop;
|
||||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Behat\Element\Shop\Account\RegisterElementInterface;
|
||||
use Sylius\Behat\Page\Shop\Account\LoginPageInterface;
|
||||
use Sylius\Behat\Page\Shop\Account\RegisterPageInterface;
|
||||
|
||||
final class AuthorizationContext implements Context
|
||||
{
|
||||
private LoginPageInterface $loginPage;
|
||||
|
||||
private RegisterPageInterface $registerPage;
|
||||
|
||||
private RegisterElementInterface $registerElement;
|
||||
|
||||
public function __construct(
|
||||
LoginPageInterface $loginPage,
|
||||
RegisterPageInterface $registerPage,
|
||||
RegisterElementInterface $registerElement
|
||||
) {
|
||||
$this->loginPage = $loginPage;
|
||||
$this->registerPage = $registerPage;
|
||||
$this->registerElement = $registerElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I sign in with email :email and password :password
|
||||
* @When I sign in again with email :email and password :password in the previous session
|
||||
*/
|
||||
public function iSignInWithEmailAndPassword(string $email, string $password): void
|
||||
{
|
||||
$this->loginPage->open();
|
||||
$this->loginPage->specifyUsername($email);
|
||||
$this->loginPage->specifyPassword($password);
|
||||
$this->loginPage->logIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I register with email :email and password :password
|
||||
*/
|
||||
public function iRegisterWithEmailAndPassword(string $email, string $password): void
|
||||
{
|
||||
$this->registerPage->open();
|
||||
$this->registerElement->specifyEmail($email);
|
||||
$this->registerElement->specifyPassword($password);
|
||||
$this->registerElement->verifyPassword($password);
|
||||
$this->registerElement->specifyFirstName('Carrot');
|
||||
$this->registerElement->specifyLastName('Ironfoundersson');
|
||||
$this->registerElement->register();
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ use Sylius\Behat\NotificationType;
|
|||
use Sylius\Behat\Page\Shop\Cart\SummaryPageInterface;
|
||||
use Sylius\Behat\Page\Shop\Product\ShowPageInterface;
|
||||
use Sylius\Behat\Service\NotificationCheckerInterface;
|
||||
use Sylius\Behat\Service\SessionManagerInterface;
|
||||
use Sylius\Behat\Service\SharedStorageInterface;
|
||||
use Sylius\Component\Product\Model\ProductInterface;
|
||||
use Sylius\Component\Product\Model\ProductOptionInterface;
|
||||
|
|
@ -34,16 +35,20 @@ final class CartContext implements Context
|
|||
|
||||
private NotificationCheckerInterface $notificationChecker;
|
||||
|
||||
private SessionManagerInterface $sessionManager;
|
||||
|
||||
public function __construct(
|
||||
SharedStorageInterface $sharedStorage,
|
||||
SummaryPageInterface $summaryPage,
|
||||
ShowPageInterface $productShowPage,
|
||||
NotificationCheckerInterface $notificationChecker
|
||||
NotificationCheckerInterface $notificationChecker,
|
||||
SessionManagerInterface $sessionManager
|
||||
) {
|
||||
$this->sharedStorage = $sharedStorage;
|
||||
$this->summaryPage = $summaryPage;
|
||||
$this->productShowPage = $productShowPage;
|
||||
$this->notificationChecker = $notificationChecker;
|
||||
$this->sessionManager = $sessionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -97,6 +102,7 @@ final class CartContext implements Context
|
|||
/**
|
||||
* @Then the grand total value should be :total
|
||||
* @Then my cart total should be :total
|
||||
* @Then their cart total should be :total
|
||||
*/
|
||||
public function myCartTotalShouldBe($total)
|
||||
{
|
||||
|
|
@ -244,13 +250,16 @@ final class CartContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Given /^an anonymous user added (product "([^"]+)") to the cart$/
|
||||
* @Given /^I (?:add|added) (this product) to the cart$/
|
||||
* @Given /^I have (product "[^"]+") added to the cart$/
|
||||
* @Given I added product :product to the cart
|
||||
* @Given he added product :product to the cart
|
||||
* @Given /^I (?:have|had) (product "[^"]+") in the cart$/
|
||||
* @Given the customer added :product product to the cart
|
||||
* @Given /^I (?:add|added) ("[^"]+" product) to the (cart)$/
|
||||
* @When I add product :product to the cart
|
||||
* @When they add product :product to the cart
|
||||
*/
|
||||
public function iAddProductToTheCart(ProductInterface $product): void
|
||||
{
|
||||
|
|
@ -271,6 +280,18 @@ final class CartContext implements Context
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^an anonymous user in another browser adds (products "([^"]+)" and "([^"]+)") to the cart$/
|
||||
*/
|
||||
public function anonymousUserAddsMultipleProductsToTheCart(array $products): void
|
||||
{
|
||||
$this->sessionManager->changeSession();
|
||||
|
||||
foreach ($products as $product) {
|
||||
$this->iAddProductToTheCart($product);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I add :variantName variant of product :product to the cart
|
||||
* @When /^I add "([^"]+)" variant of (this product) to the cart$/
|
||||
|
|
@ -364,6 +385,16 @@ final class CartContext implements Context
|
|||
Assert::true($this->summaryPage->hasItemWithCode($variantCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I view my cart in the previous session
|
||||
*/
|
||||
public function iViewMyCartInPreviousSession(): void
|
||||
{
|
||||
$this->sessionManager->restorePreviousSession();
|
||||
|
||||
$this->summaryPage->open();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given I have :product with :productOption :productOptionValue in the cart
|
||||
* @Given I have product :product with product option :productOption :productOptionValue in the cart
|
||||
|
|
|
|||
|
|
@ -74,7 +74,9 @@ final class CheckoutAddressingContext implements Context
|
|||
|
||||
/**
|
||||
* @Given /^I have completed addressing step with email "([^"]+)" and ("[^"]+" based billing address)$/
|
||||
* @Given /^they have completed addressing step with email "([^"]+)" and ("[^"]+" based billing address)$/
|
||||
* @When /^I complete addressing step with email "([^"]+)" and ("[^"]+" based billing address)$/
|
||||
* @When /^they complete addressing step with email "([^"]+)" and ("[^"]+" based billing address)$/
|
||||
*/
|
||||
public function iCompleteAddressingStepWithEmail(string $email, AddressInterface $address): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@
|
|||
<argument type="string">shop</argument>
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Behat\Service\SessionManagerInterface" class="Sylius\Behat\Service\SessionManager" public="false">
|
||||
<argument type="service" id="behat.mink" />
|
||||
<argument type="service" id="sylius.behat.shared_storage" />
|
||||
<argument type="service" id="sylius.behat.shop_security" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.shared_security" class="Sylius\Behat\Service\SharedSecurityService" public="false">
|
||||
<argument type="service" id="sylius.behat.admin_security" />
|
||||
</service>
|
||||
|
|
|
|||
|
|
@ -415,11 +415,18 @@
|
|||
<argument type="service" id="sylius.behat.notification_checker" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.ui.shop.authorization" class="Sylius\Behat\Context\Ui\Shop\AuthorizationContext">
|
||||
<argument type="service" id="sylius.behat.page.shop.account.login" />
|
||||
<argument type="service" id="sylius.behat.page.shop.account.register" />
|
||||
<argument type="service" id="sylius.behat.element.shop.account.register" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.ui.shop.cart" class="Sylius\Behat\Context\Ui\Shop\CartContext">
|
||||
<argument type="service" id="sylius.behat.shared_storage" />
|
||||
<argument type="service" id="sylius.behat.page.shop.cart_summary" />
|
||||
<argument type="service" id="sylius.behat.page.shop.product.show" />
|
||||
<argument type="service" id="sylius.behat.notification_checker" />
|
||||
<argument type="service" id="Sylius\Behat\Service\SessionManagerInterface"/>
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.ui.shop.contact" class="Sylius\Behat\Context\Ui\Shop\ContactContext">
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ default:
|
|||
- sylius.behat.context.ui.channel
|
||||
- sylius.behat.context.ui.email
|
||||
- sylius.behat.context.ui.shop.address_book
|
||||
- sylius.behat.context.ui.shop.authorization
|
||||
- sylius.behat.context.ui.shop.cart
|
||||
- sylius.behat.context.ui.shop.checkout
|
||||
- sylius.behat.context.ui.shop.checkout.addressing
|
||||
|
|
@ -60,6 +61,7 @@ default:
|
|||
- sylius.behat.context.ui.shop.currency
|
||||
- sylius.behat.context.ui.shop.homepage
|
||||
- sylius.behat.context.ui.shop.locale
|
||||
- sylius.behat.context.ui.user
|
||||
|
||||
filters:
|
||||
tags: "@checkout&&@ui"
|
||||
|
|
|
|||
84
src/Sylius/Behat/Service/SessionManager.php
Normal file
84
src/Sylius/Behat/Service/SessionManager.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Behat\Service;
|
||||
|
||||
use Behat\Mink\Mink;
|
||||
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
|
||||
|
||||
final class SessionManager implements SessionManagerInterface
|
||||
{
|
||||
private const SESSION_CHROME_HEADLESS_SECOND = 'chrome_headless_second_session';
|
||||
|
||||
private Mink $mink;
|
||||
|
||||
private SharedStorageInterface $sharedStorage;
|
||||
|
||||
private SecurityServiceInterface $securityService;
|
||||
|
||||
public function __construct(Mink $mink, SharedStorageInterface $sharedStorage, SecurityServiceInterface $securityService)
|
||||
{
|
||||
$this->mink = $mink;
|
||||
$this->sharedStorage = $sharedStorage;
|
||||
$this->securityService = $securityService;
|
||||
}
|
||||
|
||||
public function changeSession(): void
|
||||
{
|
||||
$sessionName = self::SESSION_CHROME_HEADLESS_SECOND;
|
||||
|
||||
$this->saveAndRestartSession($sessionName);
|
||||
|
||||
if ($this->sharedStorage->has($this->getKeyForToken($sessionName))) {
|
||||
$this->securityService->restoreToken($this->sharedStorage->get($this->getKeyForToken($sessionName)));
|
||||
}
|
||||
}
|
||||
|
||||
public function restorePreviousSession(): void
|
||||
{
|
||||
if (!$this->sharedStorage->has('behat_previous_session_name')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var string $sessionName */
|
||||
$sessionName = $this->sharedStorage->get('behat_previous_session_name');
|
||||
|
||||
$this->saveAndRestartSession($sessionName);
|
||||
|
||||
if ($this->sharedStorage->has($this->getKeyForToken($sessionName))) {
|
||||
$this->securityService->restoreToken($this->sharedStorage->get($this->getKeyForToken($sessionName)));
|
||||
}
|
||||
}
|
||||
|
||||
private function saveAndRestartSession(string $newSessionName): void
|
||||
{
|
||||
/** @var string $previousSessionName */
|
||||
$previousSessionName = $this->mink->getDefaultSessionName();
|
||||
|
||||
$this->sharedStorage->set('behat_previous_session_name', $previousSessionName);
|
||||
try {
|
||||
$token = $this->securityService->getCurrentToken();
|
||||
$this->sharedStorage->set($this->getKeyForToken($previousSessionName), $token);
|
||||
} catch (TokenNotFoundException $exception) {
|
||||
}
|
||||
|
||||
$this->mink->setDefaultSessionName($newSessionName);
|
||||
|
||||
$this->mink->restartSessions();
|
||||
}
|
||||
|
||||
private function getKeyForToken(string $sessionName): string
|
||||
{
|
||||
return sprintf('behat_previous_session_token_%s', $sessionName);
|
||||
}
|
||||
}
|
||||
21
src/Sylius/Behat/Service/SessionManagerInterface.php
Normal file
21
src/Sylius/Behat/Service/SessionManagerInterface.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Behat\Service;
|
||||
|
||||
interface SessionManagerInterface
|
||||
{
|
||||
public function changeSession(): void;
|
||||
|
||||
public function restorePreviousSession(): void;
|
||||
}
|
||||
131
src/Sylius/Behat/spec/Service/SessionManagerSpec.php
Normal file
131
src/Sylius/Behat/spec/Service/SessionManagerSpec.php
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Behat\Service;
|
||||
|
||||
use Behat\Mink\Mink;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Behat\Service\SecurityServiceInterface;
|
||||
use Sylius\Behat\Service\SessionManagerInterface;
|
||||
use Sylius\Behat\Service\SharedStorageInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
|
||||
final class SessionManagerSpec extends ObjectBehavior
|
||||
{
|
||||
function let(Mink $mink, SharedStorageInterface $sharedStorage, SecurityServiceInterface $securityService)
|
||||
{
|
||||
$this->beConstructedWith($mink, $sharedStorage, $securityService);
|
||||
}
|
||||
|
||||
function it_implements_session_service_interface(): void
|
||||
{
|
||||
$this->shouldImplement(SessionManagerInterface::class);
|
||||
}
|
||||
|
||||
function it_changes_session_and_does_not_restore_session_token_if_session_was_not_called_before(
|
||||
Mink $mink,
|
||||
SharedStorageInterface $sharedStorage,
|
||||
SecurityServiceInterface $securityService,
|
||||
TokenInterface $token
|
||||
): void {
|
||||
$mink->getDefaultSessionName()->willReturn('default_session');
|
||||
$securityService->getCurrentToken()->willReturn($token);
|
||||
|
||||
$token->__toString()->willReturn('{JSON_TOKEN}');
|
||||
|
||||
$sharedStorage->set('behat_previous_session_name', 'default_session')->shouldBeCalled();
|
||||
$sharedStorage->set('behat_previous_session_token_default_session', $token)->shouldBeCalled();
|
||||
|
||||
$mink->setDefaultSessionName('chrome_headless_second_session')->shouldBeCalled();
|
||||
$mink->restartSessions()->shouldBeCalled();
|
||||
|
||||
$sharedStorage->has('behat_previous_session_token_chrome_headless_second_session')->willReturn(false);
|
||||
|
||||
$securityService->restoreToken(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->changeSession();
|
||||
}
|
||||
|
||||
function it_changes_session_and_restores_session_token_if_session_was_called_before(
|
||||
Mink $mink,
|
||||
SharedStorageInterface $sharedStorage,
|
||||
SecurityServiceInterface $securityService,
|
||||
TokenInterface $token,
|
||||
TokenInterface $previousToken
|
||||
): void {
|
||||
$mink->getDefaultSessionName()->willReturn('default_session');
|
||||
$securityService->getCurrentToken()->willReturn($token);
|
||||
|
||||
$token->__toString()->willReturn('{JSON_TOKEN}');
|
||||
|
||||
$sharedStorage->set('behat_previous_session_name', 'default_session')->shouldBeCalled();
|
||||
$sharedStorage->set('behat_previous_session_token_default_session', $token)->shouldBeCalled();
|
||||
|
||||
$mink->setDefaultSessionName('chrome_headless_second_session')->shouldBeCalled();
|
||||
$mink->restartSessions()->shouldBeCalled();
|
||||
|
||||
$sharedStorage->has('behat_previous_session_token_chrome_headless_second_session')->willReturn(true);
|
||||
$sharedStorage->get('behat_previous_session_token_chrome_headless_second_session')->willReturn($previousToken);
|
||||
|
||||
$securityService->restoreToken($previousToken)->shouldBeCalled();
|
||||
|
||||
$this->changeSession();
|
||||
}
|
||||
|
||||
function it_restores_session_and_token(
|
||||
Mink $mink,
|
||||
SharedStorageInterface $sharedStorage,
|
||||
SecurityServiceInterface $securityService,
|
||||
TokenInterface $token,
|
||||
TokenInterface $defaultToken
|
||||
): void {
|
||||
$sharedStorage->has('behat_previous_session_name')->willReturn(true);
|
||||
$sharedStorage->get('behat_previous_session_name')->willReturn('previous_session');
|
||||
|
||||
$mink->getDefaultSessionName()->willReturn('default_session');
|
||||
$defaultToken->__toString()->willReturn('{JSON_DEFAULT_TOKEN}');
|
||||
|
||||
$sharedStorage->set('behat_previous_session_name', 'default_session')->shouldBeCalled();
|
||||
$sharedStorage->set('behat_previous_session_token_default_session', $defaultToken)->shouldBeCalled();
|
||||
|
||||
$mink->setDefaultSessionName('previous_session')->shouldBeCalled();
|
||||
$mink->restartSessions()->shouldBeCalled();
|
||||
|
||||
$securityService->getCurrentToken()->willReturn($defaultToken);
|
||||
$sharedStorage->has('behat_previous_session_token_previous_session')->willReturn(true);
|
||||
$sharedStorage->get('behat_previous_session_token_previous_session')->willReturn($token);
|
||||
$securityService->restoreToken($token)->shouldBeCalled();
|
||||
|
||||
$this->restorePreviousSession();
|
||||
}
|
||||
|
||||
function it_does_not_restore_session_and_token_if_previous_session_was_never_called(
|
||||
Mink $mink,
|
||||
SharedStorageInterface $sharedStorage,
|
||||
SecurityServiceInterface $securityService,
|
||||
TokenInterface $token
|
||||
): void {
|
||||
$sharedStorage->has('behat_previous_session_name')->willReturn(false);
|
||||
|
||||
$mink->getDefaultSessionName()->shouldNotBeCalled();
|
||||
$sharedStorage->set('behat_previous_session_name', 'default_session')->shouldNotBeCalled();
|
||||
|
||||
$mink->setDefaultSessionName('previous_session')->shouldNotBeCalled();
|
||||
$mink->restartSessions()->shouldNotBeCalled();
|
||||
|
||||
$securityService->restoreToken($token)->shouldNotBeCalled();
|
||||
|
||||
$this->restorePreviousSession();
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ namespace Sylius\Bundle\CoreBundle\Doctrine\ORM;
|
|||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Mapping;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Sylius\Bundle\OrderBundle\Doctrine\ORM\OrderRepository as BaseOrderRepository;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
|
|
@ -191,9 +190,11 @@ class OrderRepository extends BaseOrderRepository implements OrderRepositoryInte
|
|||
->andWhere('o.channel = :channel')
|
||||
->andWhere('o.customer = :customer')
|
||||
->andWhere('o.items IS NOT EMPTY')
|
||||
->andWhere('o.createdByGuest = :createdByGuest')
|
||||
->setParameter('state', OrderInterface::STATE_CART)
|
||||
->setParameter('channel', $channel)
|
||||
->setParameter('customer', $customer)
|
||||
->setParameter('createdByGuest', false)
|
||||
->addOrderBy('o.createdAt', 'DESC')
|
||||
->setMaxResults(1)
|
||||
->getQuery()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20220210135918 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add created_by_guest field to mark orders made by guests';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE sylius_order ADD created_by_guest TINYINT(1) DEFAULT \'1\' NOT NULL');
|
||||
$this->addSql('UPDATE sylius_order o SET o.created_by_guest = 0 WHERE o.customer_id IN (SELECT customer_id FROM sylius_shop_user)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE sylius_order DROP created_by_guest');
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,11 @@
|
|||
<field type="string" name="checkoutState" column="checkout_state" />
|
||||
<field type="string" name="paymentState" column="payment_state" />
|
||||
<field type="string" name="shippingState" column="shipping_state" />
|
||||
<field type="boolean" name="createdByGuest" column="created_by_guest" >
|
||||
<options>
|
||||
<option name="default">1</option>
|
||||
</options>
|
||||
</field>
|
||||
|
||||
<many-to-one field="channel" target-entity="Sylius\Component\Channel\Model\ChannelInterface">
|
||||
<join-column name="channel_id" referenced-column-name="id" nullable="true" />
|
||||
|
|
|
|||
|
|
@ -245,5 +245,9 @@
|
|||
</service>
|
||||
|
||||
<service id="security.authentication_manager" alias="security.authentication.manager" />
|
||||
|
||||
<service id="Sylius\Component\Core\Cart\Resolver\CreatedByGuestFlagResolverInterface" class="Sylius\Component\Core\Cart\Resolver\CreatedByGuestFlagResolver">
|
||||
<argument type="service" id="security.token_storage" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
<service id="sylius.context.cart.new_shop_based" class="Sylius\Component\Core\Cart\Context\ShopBasedCartContext" decorates="sylius.context.cart.new" decoration-priority="256" public="false">
|
||||
<argument type="service" id="sylius.context.cart.new_shop_based.inner" />
|
||||
<argument type="service" id="sylius.context.shopper" />
|
||||
<argument type="service" id="Sylius\Component\Core\Cart\Resolver\CreatedByGuestFlagResolverInterface" />
|
||||
<tag name="kernel.reset" method="reset" />
|
||||
</service>
|
||||
<service id="sylius.context.cart.customer_and_channel_based" class="Sylius\Bundle\CoreBundle\Context\CustomerAndChannelBasedCartContext">
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ final class ShopCartBlamerListener
|
|||
}
|
||||
|
||||
$cart->setCustomer($user->getCustomer());
|
||||
$cart->setByGuest(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ final class ShopCartBlamerListenerSpec extends ObjectBehavior
|
|||
$user->getCustomer()->willReturn($customer);
|
||||
|
||||
$cart->setCustomer($customer)->shouldBeCalled();
|
||||
$cart->setByGuest(false)->shouldBeCalled();
|
||||
|
||||
$this->onImplicitLogin($userEvent);
|
||||
}
|
||||
|
|
@ -110,6 +111,7 @@ final class ShopCartBlamerListenerSpec extends ObjectBehavior
|
|||
$user->getCustomer()->willReturn($customer);
|
||||
|
||||
$cart->setCustomer($customer)->shouldBeCalled();
|
||||
$cart->setByGuest(false)->shouldBeCalled();
|
||||
|
||||
$this->onInteractiveLogin(new InteractiveLoginEvent($request->getWrappedObject(), $token->getWrappedObject()));
|
||||
}
|
||||
|
|
@ -128,6 +130,7 @@ final class ShopCartBlamerListenerSpec extends ObjectBehavior
|
|||
$cart->getCustomer()->willReturn($customer);
|
||||
|
||||
$cart->setCustomer(Argument::any())->shouldNotBeCalled();
|
||||
$cart->setByGuest(false)->shouldNotBeCalled();
|
||||
|
||||
$this->onInteractiveLogin(new InteractiveLoginEvent($request->getWrappedObject(), $token->getWrappedObject()));
|
||||
}
|
||||
|
|
@ -145,6 +148,7 @@ final class ShopCartBlamerListenerSpec extends ObjectBehavior
|
|||
$token->getUser()->willReturn('anon.');
|
||||
|
||||
$cart->setCustomer(Argument::any())->shouldNotBeCalled();
|
||||
$cart->setByGuest(false)->shouldNotBeCalled();
|
||||
|
||||
$this->onInteractiveLogin(new InteractiveLoginEvent($request->getWrappedObject(), $token->getWrappedObject()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
namespace Sylius\Component\Core\Cart\Context;
|
||||
|
||||
use Sylius\Component\Channel\Context\ChannelNotFoundException;
|
||||
use Sylius\Component\Core\Cart\Resolver\CreatedByGuestFlagResolverInterface;
|
||||
use Sylius\Component\Core\Context\ShopperContextInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\CustomerInterface;
|
||||
|
|
@ -31,12 +32,22 @@ final class ShopBasedCartContext implements CartContextInterface
|
|||
|
||||
private ShopperContextInterface $shopperContext;
|
||||
|
||||
private ?CreatedByGuestFlagResolverInterface $createdByGuestFlagResolver;
|
||||
|
||||
private ?OrderInterface $cart = null;
|
||||
|
||||
public function __construct(CartContextInterface $cartContext, ShopperContextInterface $shopperContext)
|
||||
{
|
||||
public function __construct(
|
||||
CartContextInterface $cartContext,
|
||||
ShopperContextInterface $shopperContext,
|
||||
?CreatedByGuestFlagResolverInterface $createdByGuestFlagResolver = null
|
||||
) {
|
||||
$this->cartContext = $cartContext;
|
||||
$this->shopperContext = $shopperContext;
|
||||
$this->createdByGuestFlagResolver = $createdByGuestFlagResolver;
|
||||
|
||||
if ($createdByGuestFlagResolver === null) {
|
||||
@trigger_error('Not passing createdByGuestFlagResolver through constructor is deprecated in Sylius 1.10.9 and it will be prohibited in Sylius 2.0');
|
||||
}
|
||||
}
|
||||
|
||||
public function getCart(): BaseOrderInterface
|
||||
|
|
@ -72,10 +83,19 @@ final class ShopBasedCartContext implements CartContextInterface
|
|||
return $cart;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->cart = null;
|
||||
}
|
||||
|
||||
private function setCustomerAndAddressOnCart(OrderInterface $cart, CustomerInterface $customer): void
|
||||
{
|
||||
$cart->setCustomer($customer);
|
||||
|
||||
if ($this->createdByGuestFlagResolver !== null) {
|
||||
$cart->setByGuest($this->createdByGuestFlagResolver->resolveFlag());
|
||||
}
|
||||
|
||||
$defaultAddress = $customer->getDefaultAddress();
|
||||
if (null !== $defaultAddress) {
|
||||
$clonedAddress = clone $defaultAddress;
|
||||
|
|
@ -83,9 +103,4 @@ final class ShopBasedCartContext implements CartContextInterface
|
|||
$cart->setBillingAddress($clonedAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->cart = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* 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\Component\Core\Cart\Resolver;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
final class CreatedByGuestFlagResolver implements CreatedByGuestFlagResolverInterface
|
||||
{
|
||||
private TokenStorageInterface $tokenStorage;
|
||||
|
||||
public function __construct(TokenStorageInterface $tokenStorage)
|
||||
{
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
}
|
||||
|
||||
public function resolveFlag(): bool
|
||||
{
|
||||
$token = $this->tokenStorage->getToken();
|
||||
if ($token === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var UserInterface|null $user */
|
||||
$user = $token->getUser();
|
||||
|
||||
return null === $user;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* 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\Component\Core\Cart\Resolver;
|
||||
|
||||
interface CreatedByGuestFlagResolverInterface
|
||||
{
|
||||
public function resolveFlag(): bool;
|
||||
}
|
||||
|
|
@ -110,6 +110,8 @@ class Order extends BaseOrder implements OrderInterface
|
|||
*/
|
||||
protected $customerIp;
|
||||
|
||||
protected bool $createdByGuest = true;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
|
@ -460,4 +462,14 @@ class Order extends BaseOrder implements OrderInterface
|
|||
{
|
||||
$this->customerIp = $customerIp;
|
||||
}
|
||||
|
||||
public function getByGuest(): bool
|
||||
{
|
||||
return $this->createdByGuest;
|
||||
}
|
||||
|
||||
public function setByGuest(bool $createdByGuest): void
|
||||
{
|
||||
$this->createdByGuest = $createdByGuest;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@ interface OrderInterface extends
|
|||
|
||||
public function setCustomerIp(?string $customerIp): void;
|
||||
|
||||
public function getByGuest(): bool;
|
||||
|
||||
public function setByGuest(bool $guest): void;
|
||||
|
||||
/**
|
||||
* @return Collection|OrderItemInterface[]
|
||||
*
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace spec\Sylius\Component\Core\Cart\Context;
|
|||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Component\Channel\Context\ChannelNotFoundException;
|
||||
use Sylius\Component\Core\Cart\Resolver\CreatedByGuestFlagResolverInterface;
|
||||
use Sylius\Component\Core\Context\ShopperContextInterface;
|
||||
use Sylius\Component\Core\Model\AddressInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
|
|
@ -187,4 +188,70 @@ final class ShopBasedCartContextSpec extends ObjectBehavior
|
|||
$this->reset();
|
||||
$this->getCart()->shouldReturn($secondCart);
|
||||
}
|
||||
|
||||
function it_sets_created_by_guest_flag_as_false_if_order_is_created_by_logged_in_customer(
|
||||
CartContextInterface $cartContext,
|
||||
ShopperContextInterface $shopperContext,
|
||||
CreatedByGuestFlagResolverInterface $createdByGuestFlagResolver,
|
||||
OrderInterface $cart,
|
||||
ChannelInterface $channel,
|
||||
CurrencyInterface $currency,
|
||||
CustomerInterface $customer
|
||||
): void {
|
||||
$this->beConstructedWith($cartContext, $shopperContext, $createdByGuestFlagResolver);
|
||||
|
||||
$createdByGuestFlagResolver->resolveFlag()->willReturn(false);
|
||||
|
||||
$cart->setByGuest(false)->shouldBeCalled();
|
||||
|
||||
$cartContext->getCart()->shouldBeCalledTimes(1)->willReturn($cart);
|
||||
|
||||
$shopperContext->getChannel()->willReturn($channel);
|
||||
$shopperContext->getLocaleCode()->willReturn('pl');
|
||||
$shopperContext->getCustomer()->willReturn($customer);
|
||||
$customer->getDefaultAddress()->willReturn(null);
|
||||
|
||||
$channel->getBaseCurrency()->willReturn($currency);
|
||||
$currency->getCode()->willReturn('PLN');
|
||||
|
||||
$cart->setChannel($channel)->shouldBeCalled();
|
||||
$cart->setCurrencyCode('PLN')->shouldBeCalled();
|
||||
$cart->setLocaleCode('pl')->shouldBeCalled();
|
||||
$cart->setCustomer($customer)->shouldBeCalled();
|
||||
|
||||
$this->getCart()->shouldReturn($cart);
|
||||
}
|
||||
|
||||
function it_sets_created_by_guest_flag_as_true_if_order_is_created_by_anonymous_user(
|
||||
CartContextInterface $cartContext,
|
||||
ShopperContextInterface $shopperContext,
|
||||
CreatedByGuestFlagResolverInterface $createdByGuestFlagResolver,
|
||||
OrderInterface $cart,
|
||||
ChannelInterface $channel,
|
||||
CurrencyInterface $currency,
|
||||
CustomerInterface $customer
|
||||
): void {
|
||||
$this->beConstructedWith($cartContext, $shopperContext, $createdByGuestFlagResolver);
|
||||
|
||||
$createdByGuestFlagResolver->resolveFlag()->willReturn(true);
|
||||
|
||||
$cart->setByGuest(true)->shouldBeCalled();
|
||||
|
||||
$cartContext->getCart()->shouldBeCalledTimes(1)->willReturn($cart);
|
||||
|
||||
$shopperContext->getChannel()->willReturn($channel);
|
||||
$shopperContext->getLocaleCode()->willReturn('pl');
|
||||
$shopperContext->getCustomer()->willReturn($customer);
|
||||
$customer->getDefaultAddress()->willReturn(null);
|
||||
|
||||
$channel->getBaseCurrency()->willReturn($currency);
|
||||
$currency->getCode()->willReturn('PLN');
|
||||
|
||||
$cart->setChannel($channel)->shouldBeCalled();
|
||||
$cart->setCurrencyCode('PLN')->shouldBeCalled();
|
||||
$cart->setLocaleCode('pl')->shouldBeCalled();
|
||||
$cart->setCustomer($customer)->shouldBeCalled();
|
||||
|
||||
$this->getCart()->shouldReturn($cart);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Component\Core\Cart\Resolver;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Component\Core\Cart\Resolver\CreatedByGuestFlagResolverInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
final class CreatedByGuestFlagResolverSpec extends ObjectBehavior
|
||||
{
|
||||
function let(TokenStorageInterface $tokenStorage): void
|
||||
{
|
||||
$this->beConstructedWith($tokenStorage);
|
||||
}
|
||||
|
||||
function it_implements_a_created_by_guest_flag_resolver_interface(): void
|
||||
{
|
||||
$this->shouldImplement(CreatedByGuestFlagResolverInterface::class);
|
||||
}
|
||||
|
||||
function it_returns_false_if_there_is_logged_in_customer(
|
||||
TokenStorageInterface $tokenStorage,
|
||||
TokenInterface $token,
|
||||
UserInterface $user
|
||||
): void {
|
||||
$tokenStorage->getToken()->willReturn($token);
|
||||
$token->getUser()->willReturn($user);
|
||||
|
||||
$this->resolveFlag()->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_returns_true_if_order_is_created_by_anonymous_user(
|
||||
TokenStorageInterface $tokenStorage,
|
||||
TokenInterface $token
|
||||
): void {
|
||||
$tokenStorage->getToken()->willReturn($token);
|
||||
$token->getUser()->willReturn(null);
|
||||
|
||||
$this->resolveFlag()->shouldReturn(true);
|
||||
}
|
||||
|
||||
function it_returns_true_if_there_is_no_token(TokenStorageInterface $tokenStorage): void
|
||||
{
|
||||
$tokenStorage->getToken()->willReturn(null);
|
||||
|
||||
$this->resolveFlag()->shouldReturn(true);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue