Add behat page object extension

Loading services in test environment, introduce shared storage

First approach to behat steps with new workflow

[WIP] Security service for handling programatically log user in

Add custom page factory to inject router

Small refactoring in checkoutContext

Split checkout context

Update composer.lock

Relocate setup contexts

Contexts clean up and fix security issues

Create decorated channel factory

Check if user has privilege to show thank you page

Small refactoring

Get rid of container, kernel in behat contexts
This commit is contained in:
Arminek 2015-12-16 10:13:47 +01:00
parent 6246312a1d
commit d55f78dc9f
53 changed files with 2467 additions and 21 deletions

View file

@ -397,6 +397,44 @@ default:
filters:
tags: "@security"
ui-checkout:
contexts:
- Sylius\Behat\Context\Setup\ChannelContext:
defaultFranceChannelFactory: '@sylius.behat.default_france_channel_factory'
sharedStorage: '@sylius.behat.shared_storage'
- Sylius\Behat\Context\Setup\CurrencyContext:
currencyRepository: '@sylius.repository.currency'
sharedStorage: '@sylius.behat.shared_storage'
currencyFactory: '@sylius.factory.currency'
- Sylius\Behat\Context\Setup\PaymentContext:
paymentMethodRepository: '@sylius.repository.payment_method'
sharedStorage: '@sylius.behat.shared_storage'
paymentMethodFactory: '@sylius.factory.payment_method'
- Sylius\Behat\Context\Setup\SecurityContext:
sharedStorage: '@sylius.behat.shared_storage'
securityService: '@sylius.behat.security'
- Sylius\Behat\Context\Setup\UserContext:
userRepository: '@sylius.repository.user'
sharedStorage: '@sylius.behat.shared_storage'
userFactory: '@sylius.factory.user'
customerFactory: '@sylius.factory.customer'
- Sylius\Behat\Context\Setup\ProductContext:
productRepository: '@sylius.repository.product'
sharedStorage: '@sylius.behat.shared_storage'
productFactory: '@sylius.factory.product'
- Sylius\Behat\Context\Ui\CheckoutContext:
sharedStorage: '@sylius.behat.shared_storage'
productRepository: '@sylius.repository.product'
orderRepository: '@sylius.repository.order'
- Sylius\Behat\Context\Hook\DoctrineORMContext:
entityManager: '@doctrine.orm.entity_manager'
- Sylius\Behat\Context\Setup\ShippingContext:
shippingMethodRepository: '@sylius.repository.shipping_method'
shippingMethodFactory: '@sylius.factory.shipping_method'
sharedStorage: '@sylius.behat.shared_storage'
filters:
tags: "@ui-checkout"
extensions:
Lakion\Behat\MinkDebugExtension:
directory: etc/build
@ -409,6 +447,11 @@ default:
kernel:
class: TestKernel
path: app/TestKernel.php
SensioLabs\Behat\PageObjectExtension:
factory: sylius.page_object.factory
namespaces:
page: [Sylius\Behat\Page]
Sylius\Behat\Extension\SyliusPageObjectExtension: ~
gherkin:
filters:

View file

@ -95,6 +95,7 @@
"lakion/mink-debug-extension": "^1.0.1",
"lakion/api-test-case": "^1.0@dev",
"coduo/php-matcher": "^2.1@dev",
"sensiolabs/behat-page-object-extension": "^2.0@dev",
"phpunit/phpunit": "~4.1"
},
"replace": {

84
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "a5f092674e0c71c684765d612ab6b82f",
"content-hash": "64dec9562cf51236c97f1514c66b2737",
"hash": "da79832911ae4d93c0bff9440f146e76",
"content-hash": "17d2280091ae14ae467a8991886a457a",
"packages": [
{
"name": "a2lix/translation-form-bundle",
@ -2692,9 +2692,9 @@
],
"authors": [
{
"name": "Johannes Schmitt",
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh",
"homepage": "http://jmsyst.com",
"role": "Developer of wrapped JMSSerializerBundle"
}
],
@ -5692,9 +5692,9 @@
],
"authors": [
{
"name": "Johannes Schmitt",
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh",
"homepage": "http://jmsyst.com",
"role": "Developer of wrapped JMSSerializerBundle"
}
],
@ -7773,7 +7773,7 @@
],
"authors": [
{
"name": "William Durand",
"name": "William DURAND",
"email": "william.durand1@gmail.com",
"homepage": "http://www.willdurand.fr"
}
@ -9213,8 +9213,7 @@
"authors": [
{
"name": "Chris Boulton",
"homepage": "http://github.com/chrisboulton",
"role": "Original developer"
"homepage": "http://github.com/chrisboulton"
}
],
"description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).",
@ -10148,13 +10147,78 @@
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2015-06-21 13:59:46"
},
{
"name": "sensiolabs/behat-page-object-extension",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/BehatPageObjectExtension.git",
"reference": "58404070265f58ca40a13b62bb3b69b83e27de20"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sensiolabs/BehatPageObjectExtension/zipball/58404070265f58ca40a13b62bb3b69b83e27de20",
"reference": "58404070265f58ca40a13b62bb3b69b83e27de20",
"shasum": ""
},
"require": {
"behat/behat": "^3.0",
"behat/mink-extension": "^2.0",
"ocramius/proxy-manager": "^1.0",
"php": ">=5.3.0"
},
"require-dev": {
"behat/mink-goutte-driver": "~1.0",
"bossa/phpspec2-expect": "~1.0",
"phpspec/phpspec": "~2.0",
"symfony/filesystem": "^2.3|^3.0",
"symfony/process": "^2.3|^3.0"
},
"suggest": {
"bossa/phpspec2-expect": "Allows to use PHPSpec2 matchers in Behat context files"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"psr-0": {
"SensioLabs\\Behat\\PageObjectExtension\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marcello Duarte",
"email": "mduarte@inviqa.com"
},
{
"name": "Jakub Zalas",
"email": "jzalas@sensiolabs.co.uk"
}
],
"description": "Page object extension for Behat",
"homepage": "https://github.com/sensiolabs/BehatPageObjectExtension",
"keywords": [
"BDD",
"Behat",
"page"
],
"time": "2016-01-18 10:49:11"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"lakion/api-test-case": 20,
"coduo/php-matcher": 20
"coduo/php-matcher": 20,
"sensiolabs/behat-page-object-extension": 20
},
"prefer-stable": false,
"prefer-lowest": false,

View file

@ -42,8 +42,7 @@ Feature: Checkout finalization
And I select the "Dummy" radio button
And I press "Continue"
When I click "Place order"
Then I should be on the store homepage
And I should see "Thank you for your order!"
Then I should see "Thank you"
And I am on my account orders page
And I should see "All your orders"
And I should see 1 orders in the list
@ -85,5 +84,4 @@ Feature: Checkout finalization
And I select the "Dummy" radio button
And I press "Continue"
When I click "Place order"
Then I should be on the store homepage
And I should see "Thank you for your order!"
Then I should see "Thank you"

View file

@ -74,7 +74,7 @@ Feature: Checkout security
And I select the "Dummy" radio button
And I press "Continue"
And I click "Place order"
Then I should be on the store homepage
Then I should be on the checkout thank you page
And I should see "Thank you for your order!"
Scenario: Creating account without first and last name

View file

@ -0,0 +1,20 @@
@ui-checkout
Feature: Checkout with offline payment
In order to pay with cash or by external means
As a Customer
I want to be able to complete checkout process without paying
Background:
Given that store is operating on the France channel
And default currency is "EUR"
And there is user "john@example.com" identified by "password123"
And catalog has a product "PHP T-Shirt" priced at $19.99
And store has free shipping method
And store allows paying offline
Scenario: Successfully placing an order
Given I am logged in as "john@example.com"
And I added product "PHP T-Shirt" to the cart
When I proceed selecting "Offline" payment method
And I confirm my order
Then I should see the thank you page

View file

@ -65,5 +65,6 @@ suites:
UserBundle: { namespace: Sylius\Bundle\UserBundle, psr4_prefix: Sylius\Bundle\UserBundle, spec_path: src/Sylius/Bundle/UserBundle, src_path: src/Sylius/Bundle/UserBundle }
VariationBundle: { namespace: Sylius\Bundle\VariationBundle, psr4_prefix: Sylius\Bundle\VariationBundle, spec_path: src/Sylius/Bundle/VariationBundle, src_path: src/Sylius/Bundle/VariationBundle }
SyliusBehat: { namespace: Sylius\Behat, psr4_prefix: Sylius\Behat, spec_path: src/Sylius/Behat, src_path: src/Sylius/Behat }
extensions:
- Akeneo\SkipExampleExtension

View file

@ -0,0 +1,71 @@
<?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.
*/
namespace Sylius\Behat\Context;
use SensioLabs\Behat\PageObjectExtension\Context\PageObjectContext;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Behat\MinkExtension\Context\MinkAwareContext;
use Behat\Mink\Mink;
use Behat\Mink\Session;
use Behat\Mink\WebAssert;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
abstract class FeatureContext extends PageObjectContext implements MinkAwareContext
{
/**
* @var Mink
*/
protected $mink;
/**
* @var array
*/
protected $minkParameters;
/**
* {@inheritdoc}
*/
public function setMink(Mink $mink)
{
$this->mink = $mink;
}
/**
* {@inheritdoc}
*/
public function setMinkParameters(array $parameters)
{
$this->minkParameters = $parameters;
}
/**
* @param string|null $name name of the session OR active session will be used
*
* @return Session
*/
public function getSession($name = null)
{
return $this->mink->getSession($name);
}
/**
* @param string|null $name name of the session OR active session will be used
*
* @return WebAssert
*/
public function assertSession($name = null)
{
return $this->mink->assertSession($name);
}
}

View file

@ -0,0 +1,50 @@
<?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.
*/
namespace Sylius\Behat\Context\Hook;
use Behat\Behat\Context\Context;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class DoctrineORMContext implements Context
{
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* @param EntityManagerInterface $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* @BeforeScenario
*/
public function purgeDatabase()
{
if (null === $this->entityManager) {
throw new \RuntimeException('Cannot purge database. Entity manager is not set');
}
$this->entityManager->getConnection()->getConfiguration()->setSQLLogger(null);
$purger = new ORMPurger($this->entityManager);
$purger->purge();
$this->entityManager->clear();
}
}

View file

@ -0,0 +1,53 @@
<?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.
*/
namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Sylius\Component\Core\Test\Services\DefaultStoreDataInterface;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class ChannelContext implements Context
{
/**
* @var DefaultStoreDataInterface
*/
private $defaultFranceChannelFactory;
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @param DefaultStoreDataInterface $defaultFranceChannelFactory
* @param SharedStorageInterface $sharedStorage
*/
public function __construct(
DefaultStoreDataInterface $defaultFranceChannelFactory,
SharedStorageInterface $sharedStorage
) {
$this->defaultFranceChannelFactory = $defaultFranceChannelFactory;
$this->sharedStorage = $sharedStorage;
}
/**
* @Given that store is operating on the France channel
*/
public function thatStoreIsOperatingOnTheFranceChannel()
{
$defaultData = $this->defaultFranceChannelFactory->create();
$this->sharedStorage->setClipboard($defaultData);
}
}

View file

@ -0,0 +1,67 @@
<?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.
*/
namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class CurrencyContext implements Context
{
/**
* @var RepositoryInterface
*/
private $currencyRepository;
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @var FactoryInterface
*/
private $currencyFactory;
/**
* @param RepositoryInterface $currencyRepository
* @param SharedStorageInterface $sharedStorage
* @param FactoryInterface $currencyFactory
*/
public function __construct(
RepositoryInterface $currencyRepository,
SharedStorageInterface $sharedStorage,
FactoryInterface $currencyFactory
) {
$this->currencyRepository = $currencyRepository;
$this->sharedStorage = $sharedStorage;
$this->currencyFactory = $currencyFactory;
}
/**
* @Given default currency is :currencyCode
*/
public function defaultCurrencyIs($currencyCode)
{
$currency = $this->currencyFactory->createNew();
$currency->setCode($currencyCode);
$currency->setExchangeRate(1.0);
$channel = $this->sharedStorage->getCurrentResource('channel');
$channel->setDefaultCurrency($currency);
$this->currencyRepository->add($currency);
}
}

View file

@ -0,0 +1,67 @@
<?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.
*/
namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class PaymentContext implements Context
{
/**
* @var RepositoryInterface
*/
private $paymentMethodRepository;
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @var FactoryInterface
*/
private $paymentMethodFactory;
/**
* @param $paymentMethodRepository
* @param $sharedStorage
* @param $paymentMethodFactory
*/
public function __construct(RepositoryInterface $paymentMethodRepository, SharedStorageInterface $sharedStorage, FactoryInterface $paymentMethodFactory)
{
$this->paymentMethodRepository = $paymentMethodRepository;
$this->sharedStorage = $sharedStorage;
$this->paymentMethodFactory = $paymentMethodFactory;
}
/**
* @Given store allows paying offline
*/
public function storeAllowsPayingOffline()
{
$paymentMethod = $this->paymentMethodFactory->createNew();
$paymentMethod->setCode('PM1');
$paymentMethod->setGateway('dummy');
$paymentMethod->setName('Offline');
$paymentMethod->setDescription('Offline payment method');
$channel = $this->sharedStorage->getCurrentResource('channel');
$channel->addPaymentMethod($paymentMethod);
$this->paymentMethodRepository->add($paymentMethod);
}
}

View file

@ -0,0 +1,66 @@
<?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.
*/
namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class ProductContext implements Context
{
/**
* @var RepositoryInterface
*/
private $productRepository;
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @var FactoryInterface
*/
private $productFactory;
/**
* @param RepositoryInterface $productRepository
* @param SharedStorageInterface $sharedStorage
* @param FactoryInterface $productFactory
*/
public function __construct(RepositoryInterface $productRepository, SharedStorageInterface $sharedStorage, FactoryInterface $productFactory)
{
$this->productRepository = $productRepository;
$this->sharedStorage = $sharedStorage;
$this->productFactory = $productFactory;
}
/**
* @Given catalog has a product :productName priced at $:price
*/
public function catalogHasAProductPricedAt($productName, $price)
{
$product = $this->productFactory->createNew();
$product->setName($productName);
$product->setPrice((int) $price);
$product->setDescription('Awesome star wars mug');
$channel = $this->sharedStorage->getCurrentResource('channel');
$product->addChannel($channel);
$this->productRepository->add($product);
}
}

View file

@ -0,0 +1,50 @@
<?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.
*/
namespace Sylius\Behat\Context\Setup;
use Sylius\Behat\Context\FeatureContext;
use Sylius\Bundle\CoreBundle\Test\Services\SecurityServiceInterface;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class SecurityContext extends FeatureContext
{
/**
* @var SecurityServiceInterface
*/
private $securityService;
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @param SharedStorageInterface $sharedStorage
* @param SecurityServiceInterface $securityService
*/
public function __construct(SharedStorageInterface $sharedStorage, SecurityServiceInterface $securityService)
{
$this->securityService = $securityService;
$this->sharedStorage = $sharedStorage;
}
/**
* @Given I am logged in as :email
*/
public function iAmLoggedInAs($email)
{
$this->securityService->logIn($email, 'main', $this->getSession());
}
}

View file

@ -0,0 +1,72 @@
<?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.
*/
namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Sylius\Component\Shipping\Calculator\DefaultCalculators;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class ShippingContext implements Context
{
/**
* @var RepositoryInterface
*/
private $shippingMethodRepository;
/**
* @var FactoryInterface
*/
private $shippingMethodFactory;
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @param RepositoryInterface $shippingMethodRepository
* @param FactoryInterface $shippingMethodFactory
* @param SharedStorageInterface $sharedStorage
*/
public function __construct(
RepositoryInterface $shippingMethodRepository,
FactoryInterface $shippingMethodFactory,
SharedStorageInterface $sharedStorage
) {
$this->shippingMethodRepository = $shippingMethodRepository;
$this->shippingMethodFactory = $shippingMethodFactory;
$this->sharedStorage = $sharedStorage;
}
/**
* @Given store has free shipping method
*/
public function storeHasFreeShippingMethod()
{
$zone = $this->sharedStorage->getCurrentResource('zone');
$shippingMethod = $this->shippingMethodFactory->createNew();
$shippingMethod->setCode('SM1');
$shippingMethod->setName('Free');
$shippingMethod->setCurrentLocale('FR');
$shippingMethod->setConfiguration(array('amount' => 0));
$shippingMethod->setCalculator(DefaultCalculators::PER_ITEM_RATE);
$shippingMethod->setZone($zone);
$this->shippingMethodRepository->add($shippingMethod);
}
}

View file

@ -0,0 +1,83 @@
<?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.
*/
namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class UserContext implements Context
{
/**
* @var RepositoryInterface
*/
private $userRepository;
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @var FactoryInterface
*/
private $userFactory;
/**
* @var FactoryInterface
*/
private $customerFactory;
/**
* @param RepositoryInterface $userRepository
* @param SharedStorageInterface $sharedStorage
* @param FactoryInterface $userFactory
* @param FactoryInterface $customerFactory
*/
public function __construct(
RepositoryInterface $userRepository,
SharedStorageInterface $sharedStorage,
FactoryInterface $userFactory,
FactoryInterface $customerFactory
) {
$this->userRepository = $userRepository;
$this->sharedStorage = $sharedStorage;
$this->userFactory = $userFactory;
$this->customerFactory = $customerFactory;
}
/**
* @Given there is user :email identified by :password
*/
public function thereIsUserIdentifiedBy($email, $password)
{
/** @var UserInterface $user */
$user = $this->userFactory->createNew();
$customer = $this->customerFactory->createNew();
$customer->setFirstName('John');
$customer->setLastName('Doe');
$user->setCustomer($customer);
$user->setUsername('John Doe');
$user->setEmail($email);
$user->setPlainPassword($password);
$user->addRole('ROLE_USER');
$this->sharedStorage->setCurrentResource('user', $user);
$this->userRepository->add($user);
}
}

View file

@ -0,0 +1,111 @@
<?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.
*/
namespace Sylius\Behat\Context\Ui;
use Sylius\Behat\Context\FeatureContext;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Sylius\Component\Product\Model\ProductInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class CheckoutContext extends FeatureContext
{
/**
* @var SharedStorageInterface
*/
private $sharedStorage;
/**
* @var RepositoryInterface
*/
private $productRepository;
/**
* @var RepositoryInterface
*/
private $orderRepository;
/**
* @param SharedStorageInterface $sharedStorage
* @param RepositoryInterface $productRepository
* @param RepositoryInterface $orderRepository
*/
public function __construct(SharedStorageInterface $sharedStorage, RepositoryInterface $productRepository, RepositoryInterface $orderRepository)
{
$this->sharedStorage = $sharedStorage;
$this->productRepository = $productRepository;
$this->orderRepository = $orderRepository;
}
/**
* @Given I added product :name to the cart
*/
public function iAddedProductToTheCart($name)
{
/** @var ProductInterface $product */
$product = $this->productRepository->findOneBy(array('name' => $name));
$productShowPage = $this->getPage('Product\ProductShowPage')->openSpecificProductPage($product);
$productShowPage->addToCart();
}
/**
* @When I proceed selecting :paymentMethodName payment method
*/
public function iProceedSelectingOfflinePaymentMethod($paymentMethodName)
{
$checkoutAddressingPage = $this->getPage('Checkout\CheckoutAddressingStep')->open();
$addressingDetails = array(
'firstName' => 'John',
'lastName' => 'Doe',
'country' => 'France',
'street' => '0635 Myron Hollow Apt. 711',
'city' => 'North Bridget',
'postcode' => '93-554',
'phoneNumber' => '321123456'
);
$checkoutAddressingPage->fillAddressingDetails($addressingDetails);
$checkoutAddressingPage->pressButton('Continue');
$checkoutShippingPage = $this->getPage('Checkout\CheckoutShippingStep');
$checkoutShippingPage->pressRadio('Free');
$checkoutShippingPage->pressButton('Continue');
$checkoutPaymentPage = $this->getPage('Checkout\CheckoutPaymentStep');
$checkoutPaymentPage->pressRadio($paymentMethodName);
$checkoutPaymentPage->pressButton('Continue');
}
/**
* @When I confirm my order
*/
public function iConfirmMyOrder()
{
$checkoutFinalizePage = $this->getPage('Checkout\CheckoutFinalizeStep');
$checkoutFinalizePage->assertRoute();
$checkoutFinalizePage->clickLink('Place order');
}
/**
* @Then I should see the thank you page
*/
public function iShouldSeeTheThankYouPage()
{
$user = $this->sharedStorage->getCurrentResource('user');
$customer = $user->getCustomer();
$thankYouPage = $this->getPage('Checkout\CheckoutThankYouPage');
$thankYouPage->assertRoute();
$this->assertSession()->elementTextContains('css', '#thanks', sprintf('Thank you %s', $customer->getFullName()));
}
}

View file

@ -0,0 +1,65 @@
<?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.
*/
namespace Sylius\Behat\Extension\Factory;
use Behat\MinkExtension\ServiceContainer\Driver\DriverFactory;
use Sylius\Behat\Extension\SyliusPageObjectExtension;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
final class IsolatedSymfonyFactory implements DriverFactory
{
/**
* {@inheritdoc}
*/
public function getDriverName()
{
return 'symfony2';
}
/**
* {@inheritdoc}
*/
public function supportsJavascript()
{
return false;
}
/**
* {@inheritdoc}
*/
public function configure(ArrayNodeDefinition $builder)
{
}
/**
* {@inheritdoc}
*/
public function buildDriver(array $config)
{
if (!class_exists('Behat\Mink\Driver\BrowserKitDriver')) {
throw new \RuntimeException(
'Install MinkBrowserKitDriver in order to use the symfony2 driver.'
);
}
return new Definition('Behat\Symfony2Extension\Driver\KernelDriver', array(
new Reference(SyliusPageObjectExtension::DRIVER_KERNEL_ID),
'%mink.base_url%',
));
}
}

View file

@ -0,0 +1,139 @@
<?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.
*/
namespace Sylius\Behat\Extension\Factory;
use Behat\Mink\Mink;
use SensioLabs\Behat\PageObjectExtension\PageObject\Element;
use SensioLabs\Behat\PageObjectExtension\PageObject\Factory\ClassNameResolver;
use SensioLabs\Behat\PageObjectExtension\PageObject\InlineElement;
use SensioLabs\Behat\PageObjectExtension\PageObject\Page;
use SensioLabs\Behat\PageObjectExtension\PageObject\PageObject;
use Symfony\Cmf\Component\Routing\ChainRouterInterface;
use SensioLabs\Behat\PageObjectExtension\PageObject\Factory;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class PageObjectFactory implements Factory
{
/**
* @var ClassNameResolver
*/
private $classNameResolver;
/**
* @var Factory
*/
private $defaultFactory;
/**
* @var Mink
*/
private $mink = null;
/**
* @var ChainRouterInterface
*/
private $router;
/**
* @var array
*/
private $pageParameters = array();
/**
* @param ClassNameResolver $classNameResolver
* @param Factory $defaultFactory
* @param Mink $mink
* @param ChainRouterInterface $router
* @param array $pageParameters
*/
public function __construct(
ClassNameResolver $classNameResolver,
Factory $defaultFactory,
Mink $mink,
ChainRouterInterface $router,
array $pageParameters
) {
$this->classNameResolver = $classNameResolver;
$this->defaultFactory = $defaultFactory;
$this->mink = $mink;
$this->router = $router;
$this->pageParameters = $pageParameters;
}
/**
* @param string $class
*
* @return PageObject
*/
public function instantiate($class)
{
return $this->defaultFactory->instantiate($class);
}
/**
* @param string $name
*
* @return Page
*/
public function createPage($name)
{
$pageClass = $this->classNameResolver->resolvePage($name);
return $this->instantiatePage($pageClass);
}
/**
* @param string $name
*
* @return Element
*/
public function createElement($name)
{
return $this->defaultFactory->createElement($name);
}
/**
* @param string|array $selector
*
* @return InlineElement
*/
public function createInlineElement($selector)
{
return $this->defaultFactory->createInlineElement($selector);
}
/**
* @param string $pageObjectClass
*
* @return PageObject
*/
public function create($pageObjectClass)
{
return $this->defaultFactory->create($pageObjectClass);
}
/**
* @param string $pageClass
*
* @return Page
*/
private function instantiatePage($pageClass)
{
if (!is_subclass_of($pageClass, 'Sylius\Behat\Page\SymfonyPage')) {
throw new \InvalidArgumentException(sprintf('Invalid page object class: %s, to use this factory you need to extend SymfonyPage', $pageClass));
}
return new $pageClass($this->mink->getSession(), $this, $this->pageParameters, $this->router);
}
}

View file

@ -0,0 +1,78 @@
<?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.
*/
namespace Sylius\Behat\Extension;
use Behat\Symfony2Extension\ServiceContainer\Symfony2Extension;
use Behat\Testwork\ServiceContainer\Extension as TestworkExtension;
use Behat\Testwork\ServiceContainer\ExtensionManager;
use Sylius\Behat\Extension\Factory\IsolatedSymfonyFactory;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class SyliusPageObjectExtension implements TestworkExtension
{
const DRIVER_KERNEL_ID = 'sylius_extension.kernel';
/**
* {@inheritdoc}
*/
public function getConfigKey()
{
return 'sylius_page_object';
}
/**
* {@inheritdoc}
*/
public function initialize(ExtensionManager $extensionManager)
{
if (null !== $minkExtension = $extensionManager->getExtension('mink')) {
$minkExtension->registerDriverFactory(new IsolatedSymfonyFactory());
}
}
/**
* {@inheritdoc}
*/
public function configure(ArrayNodeDefinition $builder)
{
}
/**
* {@inheritdoc}
*/
public function load(ContainerBuilder $container, array $config)
{
$container->setDefinition(self::DRIVER_KERNEL_ID, $container->getDefinition(Symfony2Extension::KERNEL_ID));
}
/**
* {@inheritdoc}S
*/
public function process(ContainerBuilder $container)
{
$classNameResolver = $container->get('sensio_labs.page_object_extension.class_name_resolver.camelcased');
$defaultFactory = $container->get('sensio_labs.page_object_extension.page_factory.default');
$kernel = $container->get('symfony2_extension.kernel');
$appContainer = clone $kernel->getContainer();
$router = $appContainer->get('router');
$definition = new Definition('Sylius\Behat\Extension\Factory\PageObjectFactory');
$definition->setArguments(array($classNameResolver, $defaultFactory, new Reference('mink'), $router, '%mink.parameters%'));
$container->setDefinition('sylius.page_object.factory', $definition);
}
}

View file

@ -0,0 +1,45 @@
<?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.
*/
namespace Sylius\Behat\Page\Checkout;
use Sylius\Behat\Page\SymfonyPage;
use Behat\Mink\Exception\ElementNotFoundException;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class CheckoutAddressingStep extends SymfonyPage
{
/**
* @return string
*/
public function getRouteName()
{
return 'sylius_checkout_addressing';
}
/**
* @param array $addressingDetails
*
* @throws ElementNotFoundException
*/
public function fillAddressingDetails(array $addressingDetails)
{
$this->fillField('First name', $addressingDetails['firstName']);
$this->fillField('Last name', $addressingDetails['lastName']);
$this->selectFieldOption('Country', $addressingDetails['country']);
$this->fillField('Street', $addressingDetails['street']);
$this->fillField('City', $addressingDetails['city']);
$this->fillField('Postcode', $addressingDetails['postcode']);
$this->fillField('Phone number', $addressingDetails['phoneNumber']);
}
}

View file

@ -0,0 +1,28 @@
<?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.
*/
namespace Sylius\Behat\Page\Checkout;
use Sylius\Behat\Page\SymfonyPage;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class CheckoutFinalizeStep extends SymfonyPage
{
/**
* @return string
*/
public function getRouteName()
{
return 'sylius_checkout_finalize';
}
}

View file

@ -0,0 +1,28 @@
<?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.
*/
namespace Sylius\Behat\Page\Checkout;
use Sylius\Behat\Page\SymfonyPage;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class CheckoutPaymentStep extends SymfonyPage
{
/**
* @return string
*/
public function getRouteName()
{
return 'sylius_checkout_payment';
}
}

View file

@ -0,0 +1,28 @@
<?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.
*/
namespace Sylius\Behat\Page\Checkout;
use Sylius\Behat\Page\SymfonyPage;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class CheckoutShippingStep extends SymfonyPage
{
/**
* @return string
*/
public function getRouteName()
{
return 'sylius_checkout_shipping';
}
}

View file

@ -0,0 +1,28 @@
<?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.
*/
namespace Sylius\Behat\Page\Checkout;
use Sylius\Behat\Page\SymfonyPage;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class CheckoutThankYouPage extends SymfonyPage
{
/**
* @return string
*/
public function getRouteName()
{
return 'sylius_checkout_thank_you';
}
}

View file

@ -0,0 +1,48 @@
<?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.
*/
namespace Sylius\Behat\Page\Product;
use SensioLabs\Behat\PageObjectExtension\PageObject\Factory;
use Sylius\Behat\Page\SymfonyPage;
use Sylius\Component\Product\Model\ProductInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class ProductShowPage extends SymfonyPage
{
/**
* @param ProductInterface $product
*
* @return ProductShowPage
*/
public function openSpecificProductPage(ProductInterface $product)
{
$url = $this->router->generate($product);
$this->getSession()->visit($url);
return $this;
}
public function addToCart()
{
$this->pressButton('Add to cart');
}
/**
* @return null
*/
public function getRouteName()
{
return null;
}
}

View file

@ -0,0 +1,82 @@
<?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.
*/
namespace Sylius\Behat\Page;
use Behat\Mink\Session;
use SensioLabs\Behat\PageObjectExtension\PageObject\Exception\ElementNotFoundException;
use SensioLabs\Behat\PageObjectExtension\PageObject\Factory;
use SensioLabs\Behat\PageObjectExtension\PageObject\Page;
use Symfony\Component\Routing\RouterInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
abstract class SymfonyPage extends Page
{
/**
* @var RouterInterface
*/
protected $router;
/**
* @param Session $session
* @param Factory $factory
* @param array $parameters
* @param RouterInterface $router
*/
public function __construct(Session $session, Factory $factory, array $parameters = array(), RouterInterface $router)
{
parent::__construct($session, $factory, $parameters);
$this->router = $router;
}
/**
* @param string $locator
*
* @throws ElementNotFoundException
*/
public function pressRadio($locator)
{
$radio = $this->findField($locator);
if (null === $radio) {
throw new ElementNotFoundException(sprintf('"%s" element is not present on the page', $locator));
}
$this->fillField($radio->getAttribute('name'), $radio->getAttribute('value'));
}
/**
* @param array $urlParameters
*/
public function assertRoute(array $urlParameters = array())
{
$this->verify($urlParameters);
}
/**
* @param array $urlParameters
*
* @return string
*/
protected function getUrl(array $urlParameters = array())
{
if (null === $this->getRouteName()) {
throw new \RuntimeException('You need to provide route name, null given');
}
return $this->router->generate($this->getRouteName(), $urlParameters, true);
}
abstract public function getRouteName();
}

View file

@ -0,0 +1,54 @@
<?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.
*/
namespace spec\Sylius\Behat\Context\Setup;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Test\Services\DefaultStoreDataInterface;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class ChannelContextSpec extends ObjectBehavior
{
function let(
DefaultStoreDataInterface $defaultFranceChannelFactory,
SharedStorageInterface $sharedStorage
) {
$this->beConstructedWith($defaultFranceChannelFactory, $sharedStorage);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Behat\Context\Setup\ChannelContext');
}
function it_is_context()
{
$this->shouldImplement('Behat\Behat\Context\Context');
}
function it_sets_default_france_channel_in_to_shared_storage(
$defaultFranceChannelFactory,
$sharedStorage,
ChannelInterface $channel,
ZoneInterface $zone
) {
$defaultData = array('channel' => $channel, 'zone' => $zone);
$defaultFranceChannelFactory->create()->willReturn($defaultData);
$sharedStorage->setClipboard($defaultData)->shouldBeCalled();
$this->thatStoreIsOperatingOnTheFranceChannel();
}
}

View file

@ -0,0 +1,50 @@
<?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.
*/
namespace Sylius\Bundle\ChannelBundle\DependencyInjection\Compiler;
use Sylius\Component\Resource\Factory\Factory;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Parameter;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class RegisterChannelFactoryPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('sylius.factory.channel')) {
return;
}
$baseChannelFactoryDefinition = new Definition(
Factory::class,
[
new Parameter('sylius.model.channel.class'),
]
);
$channelFactoryDefinition = new Definition(
$container->getParameter('sylius.factory.channel.class'),
[
$baseChannelFactoryDefinition,
]
);
$container->setDefinition('sylius.factory.channel', $channelFactoryDefinition);
}
}

View file

@ -15,6 +15,7 @@ use Sylius\Bundle\ChannelBundle\Form\Type\ChannelType;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Bundle\ResourceBundle\Form\Type\ResourceChoiceType;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Sylius\Component\Channel\Factory\ChannelFactory;
use Sylius\Component\Channel\Model\Channel;
use Sylius\Component\Channel\Model\ChannelInterface;
use Sylius\Component\Resource\Factory\Factory;
@ -72,7 +73,7 @@ class Configuration implements ConfigurationInterface
->scalarNode('interface')->defaultValue(ChannelInterface::class)->cannotBeEmpty()->end()
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
->scalarNode('repository')->cannotBeEmpty()->end()
->scalarNode('factory')->defaultValue(Factory::class)->end()
->scalarNode('factory')->defaultValue(ChannelFactory::class)->end()
->arrayNode('form')
->addDefaultsIfNotSet()
->children()

View file

@ -11,9 +11,11 @@
namespace Sylius\Bundle\ChannelBundle;
use Sylius\Bundle\ChannelBundle\DependencyInjection\Compiler\RegisterChannelFactoryPass;
use Sylius\Bundle\ResourceBundle\AbstractResourceBundle;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Sylius\Component\Channel\Model\ChannelInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Channels bundle.
@ -32,6 +34,16 @@ class SyliusChannelBundle extends AbstractResourceBundle
);
}
/**
* {@inheritdoc}
*/
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new RegisterChannelFactoryPass());
}
/**
* {@inheritdoc}
*/

View file

@ -0,0 +1,88 @@
<?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.
*/
namespace spec\Sylius\Bundle\ChannelBundle\DependencyInjection\Compiler;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Channel\Factory\ChannelFactory;
use Sylius\Component\Resource\Factory\Factory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class RegisterChannelFactoryPassSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ChannelBundle\DependencyInjection\Compiler\RegisterChannelFactoryPass');
}
function it_implements_compiler_pass_interface()
{
$this->shouldImplement(CompilerPassInterface::class);
}
function it_creates_default_definition_of_channel_factory(
ContainerBuilder $container
) {
$container->hasDefinition('sylius.factory.channel')->willReturn(true);
$baseChannelFactoryDefinition = new Definition(
Factory::class,
[
new Parameter('sylius.model.channel.class'),
]
);
$container->getParameter('sylius.factory.channel.class')->willReturn(ChannelFactory::class);
$channelFactoryDefinition = new Definition(
ChannelFactory::class,
[
$baseChannelFactoryDefinition,
]
);
$container->setDefinition('sylius.factory.channel', $channelFactoryDefinition)->shouldBeCalled();
$this->process($container);
}
function it_does_not_create_default_definition_of_channel_factory_if_channel_factory_is_not_set(
ContainerBuilder $container
) {
$container->hasDefinition('sylius.factory.channel')->willReturn(false);
$baseChannelFactoryDefinition = new Definition(
Factory::class,
[
new Parameter('sylius.model.channel.class'),
]
);
$container->getParameter('sylius.factory.channel.class')->shouldNotBeCalled();
$channelFactoryDefinition = new Definition(
ChannelFactory::class,
[
$baseChannelFactoryDefinition,
]
);
$container->setDefinition('sylius.factory.channel', $channelFactoryDefinition)->shouldNotBeCalled();
$this->process($container);
}
}

View file

@ -50,7 +50,7 @@ class CheckoutProcessScenario implements ProcessScenarioInterface
->add('purchase', 'sylius_checkout_purchase')
->setDisplayRoute('sylius_checkout_display')
->setForwardRoute('sylius_checkout_forward')
->setRedirect('sylius_homepage')
->setRedirect('sylius_checkout_thank_you')
->validate(function () {
return !$this->getCurrentCart()->isEmpty();
})

View file

@ -63,6 +63,7 @@ class FinalizeStep extends CheckoutStep
*/
protected function completeOrder(OrderInterface $order)
{
$this->get('session')->set('sylius_order_id', $order->getId());
$this->dispatchCheckoutEvent(SyliusOrderEvents::PRE_CREATE, $order);
$this->dispatchCheckoutEvent(SyliusCheckoutEvents::FINALIZE_PRE_COMPLETE, $order);

View file

@ -193,6 +193,19 @@ class CheckoutController extends FOSRestController
}
/**
* @return Response
*/
public function thankYouAction()
{
$id = $this->get('session')->get('sylius_order_id');
$order = $this->findOrderOr404($id);
return $this->render('SyliusWebBundle:Frontend/Checkout/Step:thankYou.html.twig', array('order' => $order));
}
/**
* Get object manager.
*
* @return ObjectManager
*/
protected function getManager()

View file

@ -79,6 +79,11 @@ class SyliusCoreExtension extends AbstractResourceExtension implements PrependEx
'email.xml',
);
$env = $container->getParameter('kernel.environment');
if ('test' === $env || 'test_cached' === $env) {
$configFiles[] = 'test_services.xml';
}
foreach ($configFiles as $configFile) {
$loader->load($configFile);
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sylius.behat.shared_storage.class">Sylius\Component\Core\Test\Services\SharedStorage</parameter>
<parameter key="sylius.behat.security.class">Sylius\Bundle\CoreBundle\Test\Services\SecurityService</parameter>
<parameter key="sylius.behat.default_france_channel_factory.class">Sylius\Component\Core\Test\Services\DefaultFranceChannelFactory</parameter>
</parameters>
<services>
<service id="sylius.behat.shared_storage" class="%sylius.behat.shared_storage.class%" />
<service id="sylius.behat.security" class="%sylius.behat.security.class%">
<argument type="service" id="sylius.repository.user" />
<argument type="service" id="session" />
</service>
<!--Default Store Data-->
<service id="sylius.behat.default_france_channel_factory" class="%sylius.behat.default_france_channel_factory.class%">
<argument type="service" id="sylius.repository.channel" />
<argument type="service" id="sylius.repository.country" />
<argument type="service" id="sylius.repository.zone_member" />
<argument type="service" id="sylius.repository.zone" />
<argument type="service" id="sylius.factory.channel" />
<argument type="service" id="sylius.factory.country" />
<argument type="service" id="sylius.factory.zone_member" />
<argument type="service" id="sylius.factory.zone" />
</service>
</services>
</container>

View file

@ -0,0 +1,63 @@
<?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.
*/
namespace Sylius\Bundle\CoreBundle\Test\Services;
use Behat\Mink\Session;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class SecurityService implements SecurityServiceInterface
{
/**
* @var UserRepositoryInterface
*/
private $userRepository;
/**
* @var SessionInterface
*/
private $session;
/**
* {@inheritdoc}
*/
public function __construct(
UserRepositoryInterface $userRepository,
SessionInterface $session
) {
$this->userRepository = $userRepository;
$this->session = $session;
}
/**
* {@inheritdoc}
*/
public function logIn($email, $providerKey, Session $minkSession)
{
$user = $this->userRepository->findOneBy(array('username' => $email));
if (null === $user) {
throw new \InvalidArgumentException(sprintf('There is no user with email %s', $email));
}
$token = new UsernamePasswordToken($user, $user->getPassword(), $providerKey, $user->getRoles());
$this->session->set('_security_user', serialize($token));
$this->session->save();
$minkSession->setCookie($this->session->getName(), $this->session->getId());
}
}

View file

@ -0,0 +1,29 @@
<?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.
*/
namespace Sylius\Bundle\CoreBundle\Test\Services;
use Behat\Mink\Session;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
interface SecurityServiceInterface
{
/**
* @param string $email
* @param string $providerKey
* @param Session $minkSession
*
* @throws \InvalidArgumentException
*/
public function logIn($email, $providerKey, Session $minkSession);
}

View file

@ -40,19 +40,21 @@ class CheckoutProcessScenarioSpec extends ObjectBehavior
$this->shouldImplement(ProcessScenarioInterface::class);
}
function it_builds_checkout_process_with_proper_steps(ProcessBuilderInterface $builder)
function it_builds_checkout_process_with_proper_steps(ProcessBuilderInterface $builder, $cart)
{
$builder->add('security', 'sylius_checkout_security')->willReturn($builder)->shouldBeCalled();
$builder->add('addressing', 'sylius_checkout_addressing')->willReturn($builder)->shouldBeCalled();
$builder->add('shipping', 'sylius_checkout_shipping')->willReturn($builder)->shouldBeCalled();
$builder->add('payment', 'sylius_checkout_payment')->willReturn($builder)->shouldBeCalled();
$builder->add('finalize', 'sylius_checkout_finalize')->willReturn($builder)->shouldBeCalled();
$builder->add("purchase", "sylius_checkout_purchase")->willReturn($builder)->shouldBeCalled();
$builder->add('purchase', 'sylius_checkout_purchase')->willReturn($builder)->shouldBeCalled();
$builder->setRedirect('sylius_checkout_thank_you')->willReturn($builder)->shouldBeCalled();
$cart->getId()->willReturn(1);
$builder->setDisplayRoute('sylius_checkout_display')->willReturn($builder)->shouldBeCalled();
$builder->setForwardRoute('sylius_checkout_forward')->willReturn($builder)->shouldBeCalled();
$builder->setRedirect('sylius_homepage')->willReturn($builder)->shouldBeCalled();
$builder->validate(Argument::any())->willReturn($builder)->shouldBeCalled();
$this->build($builder);

View file

@ -0,0 +1,85 @@
<?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.
*/
namespace spec\Sylius\Bundle\CoreBundle\Test\Services;
use Behat\Mink\Session;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Component\User\Model\UserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class SecurityServiceSpec extends ObjectBehavior
{
function let(
UserRepositoryInterface $userRepository,
SessionInterface $session
) {
$this->beConstructedWith($userRepository, $session);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\CoreBundle\Test\Services\SecurityService');
}
function it_implements_security_service_interface()
{
$this->shouldImplement('Sylius\Bundle\CoreBundle\Test\Services\SecurityServiceInterface');
}
function it_logs_user_in(
$userRepository,
$session,
UserInterface $user,
Session $minkSession
) {
$userRoles = ['ROLE_USER'];
$userRepository->findOneBy(array('username' => 'sylius@example.com'))->willReturn($user);
$user->getRoles()->willReturn($userRoles);
$user->getPassword()->willReturn('xyz');
$user->serialize()->willReturn('serialized_user');
$session->set('_security_user', Argument::any())->shouldBeCalled();
$session->save()->shouldBeCalled();
$session->getName()->willReturn('MOCKEDSID');
$session->getId()->willReturn('xyzc123');
$minkSession->setCookie('MOCKEDSID', 'xyzc123')->shouldBeCalled();
$this->logIn('sylius@example.com', 'default', $minkSession);
}
function it_does_not_log_user_in_if_user_was_not_found(
$userRepository,
$session,
UserInterface $user,
Session $minkSession
) {
$userRoles = ['ROLE_USER'];
$userRepository->findOneBy(array('username' => 'sylius@example.com'))->willReturn(null);
$user->getRoles()->willReturn($userRoles);
$user->getPassword()->willReturn('xyz');
$user->serialize()->willReturn('serialized_user');
$session->set('_security_user', Argument::any())->shouldNotBeCalled();
$session->save()->shouldNotBeCalled();
$session->getName()->willReturn('MOCKEDSID');
$session->getId()->willReturn('xyzc123');
$minkSession->setCookie('MOCKEDSID', 'xyzc123')->shouldNotBeCalled();
$this->shouldThrow(new \InvalidArgumentException(sprintf('There is no user with email sylius@example.com')))->during('logIn', array('sylius@example.com', 'default', $minkSession));
}
}

View file

@ -613,7 +613,7 @@ class WebContext extends BaseWebContext implements SnippetAcceptingContext
$this->iSelectTheRadioButton('Credit Card');
$this->pressButton('Continue');
$this->iClick('Place order');
$this->assertSession()->pageTextContains('Thank you for your order!');
$this->assertSession()->pageTextContains('Thank you');
}
/**

View file

@ -5,6 +5,11 @@ sylius_checkout_start:
path: /
defaults: { _controller: sylius.controller.process:startAction, scenarioAlias: 'sylius_checkout' }
sylius_checkout_thank_you:
path: /thank-you
methods: [GET]
defaults: { _controller: sylius.controller.checkout:thankYouAction }
sylius_checkout_display:
path: /{stepName}
defaults: { _controller: sylius.controller.process:displayAction, scenarioAlias: 'sylius_checkout' }

View file

@ -0,0 +1,5 @@
{% extends 'SyliusWebBundle:Frontend:layout.html.twig' %}
{% block content %}
<h1 id="thanks">Thank you {{ order.customer.fullName }}</h1>
{% endblock %}

View file

@ -0,0 +1,49 @@
<?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.
*/
namespace Sylius\Component\Channel\Factory;
use Sylius\Component\Resource\Factory\FactoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class ChannelFactory implements ChannelFactoryInterface
{
private $defaultFactory;
/**
* {@inheritdoc}
*/
public function __construct(FactoryInterface $defaultFactory)
{
$this->defaultFactory = $defaultFactory;
}
/**
* {@inheritdoc}
*/
public function createNew()
{
return $this->defaultFactory->createNew();
}
/**
* {@inheritdoc}
*/
public function createNamed($name)
{
$channel = $this->defaultFactory->createNew();
$channel->setName($name);
return $channel;
}
}

View file

@ -0,0 +1,28 @@
<?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.
*/
namespace Sylius\Component\Channel\Factory;
use Sylius\Component\Channel\Model\ChannelInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
interface ChannelFactoryInterface extends FactoryInterface
{
/**
* @param string $name
*
* @return ChannelInterface
*/
public function createNamed($name);
}

View file

@ -0,0 +1,54 @@
<?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.
*/
namespace spec\Sylius\Component\Channel\Factory;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Channel\Model\ChannelInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class ChannelFactorySpec extends ObjectBehavior
{
function let(
FactoryInterface $defaultFactory
) {
$this->beConstructedWith($defaultFactory);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Component\Channel\Factory\ChannelFactory');
}
function it_implements_channel_factory_interface()
{
$this->shouldImplement('Sylius\Component\Channel\Factory\ChannelFactoryInterface');
}
function it_creates_channel_with_name($defaultFactory, ChannelInterface $channel)
{
$defaultFactory->createNew()->willReturn($channel);
$channel->setName('United States Webstore')->shouldBeCalled();
$this->createNamed('United States Webstore')->shouldReturn($channel);
}
function it_creates_empty_channel($defaultFactory, ChannelInterface $channel)
{
$defaultFactory->createNew()->willReturn($channel);
$this->createNew()->shouldReturn($channel);
}
}

View file

@ -0,0 +1,160 @@
<?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.
*/
namespace Sylius\Component\Core\Test\Services;
use Sylius\Component\Addressing\Model\CountryInterface;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Addressing\Model\ZoneMemberInterface;
use Sylius\Component\Channel\Factory\ChannelFactoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class DefaultFranceChannelFactory implements DefaultStoreDataInterface
{
/**
* @var RepositoryInterface
*/
private $channelRepository;
/**
* @var RepositoryInterface
*/
private $countryRepository;
/**
* @var RepositoryInterface
*/
private $zoneMemberRepository;
/**
* @var RepositoryInterface
*/
private $zoneRepository;
/**
* @var ChannelFactoryInterface
*/
private $channelFactory;
/**
* @var FactoryInterface
*/
private $countryFactory;
/**
* @var FactoryInterface
*/
private $zoneMemberFactory;
/**
* @var FactoryInterface
*/
private $zoneFactory;
/**
* @param RepositoryInterface $channelRepository
* @param RepositoryInterface $countryRepository
* @param RepositoryInterface $zoneMemberRepository
* @param RepositoryInterface $zoneRepository
* @param FactoryInterface $channelFactory
* @param FactoryInterface $countryFactory
* @param FactoryInterface $zoneMemberFactory
* @param FactoryInterface $zoneFactory
*/
public function __construct(
RepositoryInterface $channelRepository,
RepositoryInterface $countryRepository,
RepositoryInterface $zoneMemberRepository,
RepositoryInterface $zoneRepository,
FactoryInterface $channelFactory,
FactoryInterface $countryFactory,
FactoryInterface $zoneMemberFactory,
FactoryInterface $zoneFactory
) {
$this->channelRepository = $channelRepository;
$this->countryRepository = $countryRepository;
$this->zoneMemberRepository = $zoneMemberRepository;
$this->zoneRepository = $zoneRepository;
$this->channelFactory = $channelFactory;
$this->countryFactory = $countryFactory;
$this->zoneMemberFactory = $zoneMemberFactory;
$this->zoneFactory = $zoneFactory;
}
public function create()
{
$defaultData['channel'] = $this->createChannel();
$defaultData['country'] = $this->createCountry();
$defaultData['zone_member'] = $this->createZoneMember();
$defaultData['zone'] = $this->createZone($defaultData['zone_member']);
$this->channelRepository->add($defaultData['channel']);
$this->countryRepository->add($defaultData['country']);
$this->zoneRepository->add($defaultData['zone']);
$this->zoneMemberRepository->add($defaultData['zone_member']);
return $defaultData;
}
/**
* @return ChannelInterface
*/
private function createChannel()
{
$channel = $this->channelFactory->createNamed('France');
$channel->setCode('WEB-FR');
return $channel;
}
/**
* @return CountryInterface
*/
private function createCountry()
{
$country = $this->countryFactory->createNew();
$country->setCode('FR');
return $country;
}
/**
* @return ZoneMemberInterface
*/
private function createZoneMember()
{
$zoneMember = $this->zoneMemberFactory->createNew();
$zoneMember->setCode('FR');
return $zoneMember;
}
/**
* @param ZoneMemberInterface $zoneMember
*
* @return ZoneInterface
*/
private function createZone(ZoneMemberInterface $zoneMember)
{
$zone = $this->zoneFactory->createNew();
$zone->setCode('FR');
$zone->setName('France');
$zone->setType(ZoneInterface::TYPE_COUNTRY);
$zone->addMember($zoneMember);
return $zone;
}
}

View file

@ -0,0 +1,20 @@
<?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.
*/
namespace Sylius\Component\Core\Test\Services;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
interface DefaultStoreDataInterface
{
public function create();
}

View file

@ -0,0 +1,75 @@
<?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.
*/
namespace Sylius\Component\Core\Test\Services;
use Sylius\Component\Resource\Model\ResourceInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class SharedStorage implements SharedStorageInterface
{
/**
* @var array
*/
private $clipboard = array();
/**
* @var string|null
*/
private $latestKey = null;
/**
* {@inheritdoc}
*/
public function setCurrentResource($key, ResourceInterface $resource, $override = false)
{
if (isset($this->clipboard[$key]) && !$override) {
throw new \RuntimeException('This key is already used, if you want override set override flag');
}
$this->clipboard[$key] = $resource;
$this->latestKey = $key;
}
/**
* {@inheritdoc}
*/
public function getCurrentResource($key)
{
if (!isset($this->clipboard[$key])) {
throw new \InvalidArgumentException(sprintf('There is no current resource for "%s"!', $key));
}
return $this->clipboard[$key];
}
/**
* {@inheritdoc}
*/
public function getLatestResource()
{
return $this->clipboard[$this->latestKey];
}
/**
* {@inheritdoc}
*/
public function setClipboard(array $clipboard, $override = false)
{
if (!empty($this->clipboard) && !$override) {
throw new \RuntimeException('Clipboard is not empty, if you want override set override flag');
}
$this->clipboard = $clipboard;
}
}

View file

@ -0,0 +1,44 @@
<?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.
*/
namespace Sylius\Component\Core\Test\Services;
use Sylius\Component\Resource\Model\ResourceInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
interface SharedStorageInterface
{
/**
* @param string $key
*
* @return ResourceInterface
*/
public function getCurrentResource($key);
/**
* @param string $key
* @param ResourceInterface $resource
* @param bool $override
*
* @throws \RuntimeException
*/
public function setCurrentResource($key, ResourceInterface $resource, $override = false);
/**
* @param array $clipboard
* @param bool $override
*
* @throws \RuntimeException
*/
public function setClipboard(array $clipboard, $override = false);
}

View file

@ -0,0 +1,95 @@
<?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.
*/
namespace spec\Sylius\Component\Core\Test\Services;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Addressing\Model\CountryInterface;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Addressing\Model\ZoneMemberInterface;
use Sylius\Component\Channel\Factory\ChannelFactoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class DefaultFranceChannelFactorySpec extends ObjectBehavior
{
function let(
RepositoryInterface $channelRepository,
RepositoryInterface $countryRepository,
RepositoryInterface $zoneMemberRepository,
RepositoryInterface $zoneRepository,
ChannelFactoryInterface $channelFactory,
FactoryInterface $countryFactory,
FactoryInterface $zoneMemberFactory,
FactoryInterface $zoneFactory
) {
$this->beConstructedWith(
$channelRepository,
$countryRepository,
$zoneMemberRepository,
$zoneRepository,
$channelFactory,
$countryFactory,
$zoneMemberFactory,
$zoneFactory
);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Component\Core\Test\Services\DefaultFranceChannelFactory');
}
function it_is_default_store_data()
{
$this->shouldImplement('Sylius\Component\Core\Test\Services\DefaultStoreDataInterface');
}
function it_creates_france_country_and_zone(
$channelRepository,
$countryRepository,
$zoneMemberRepository,
$zoneRepository,
$channelFactory,
$countryFactory,
$zoneMemberFactory,
$zoneFactory,
CountryInterface $country,
ZoneMemberInterface $zoneMember,
ZoneInterface $zone,
ChannelInterface $channel
) {
$channel->getName()->willReturn('France');
$channelFactory->createNamed('France')->willReturn($channel);
$countryFactory->createNew()->willReturn($country);
$zoneMemberFactory->createNew()->willReturn($zoneMember);
$zoneFactory->createNew()->willReturn($zone);
$channel->setCode('WEB-FR')->shouldBeCalled();
$country->setCode('FR')->shouldBeCalled();
$zoneMember->setCode('FR')->shouldBeCalled();
$zone->setCode('FR')->shouldBeCalled();
$zone->setName('France')->shouldBeCalled();
$zone->setType(ZoneInterface::TYPE_COUNTRY)->shouldBeCalled();
$zone->addMember($zoneMember)->shouldBeCalled();
$channelRepository->add($channel)->shouldBeCalled();
$countryRepository->add($country)->shouldBeCalled();
$zoneRepository->add($zone)->shouldBeCalled();
$zoneMemberRepository->add($zoneMember)->shouldBeCalled();
$this->create();
}
}

View file

@ -0,0 +1,77 @@
<?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.
*/
namespace spec\Sylius\Component\Core\Test\Services;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductInterface;
/**
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com>
*/
class SharedStorageSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Component\Core\Test\Services\SharedStorage');
}
function it_implements_shared_storage_interface()
{
$this->shouldImplement('Sylius\Component\Core\Test\Services\SharedStorageInterface');
}
function it_has_resources_in_clipboard(ChannelInterface $channel, ProductInterface $product)
{
$this->setCurrentResource('channel1', $channel);
$this->getCurrentResource('channel1')->shouldReturn($channel);
$this->setCurrentResource('product1', $product);
$this->getCurrentResource('product1')->shouldReturn($product);
}
function it_returns_latest_added_resource(ChannelInterface $channel, ProductInterface $product)
{
$this->setCurrentResource('channel1', $channel);
$this->setCurrentResource('product1', $product);
$this->getLatestResource()->shouldReturn($product);
}
function it_prevents_setting_resource_at_used_key(ChannelInterface $channel, ChannelInterface $channel1)
{
$this->setCurrentResource('channel', $channel);
$this->shouldThrow(new \RuntimeException('This key is already used, if you want override set override flag'))->during('setCurrentResource', array('channel', $channel1));
}
function it_overrides_existing_resource_key(ChannelInterface $channel, ChannelInterface $channel1)
{
$this->setCurrentResource('channel', $channel);
$this->setCurrentResource('channel', $channel1, true);
$this->getCurrentResource('channel')->shouldReturn($channel1);
}
function it_prevents_setting_clipboard_if_it_is_not_empty(ChannelInterface $channel)
{
$this->setCurrentResource('channel', $channel);
$this->shouldThrow(new \RuntimeException('Clipboard is not empty, if you want override set override flag'))->during('setClipboard', array(array()));
}
function it_overrides_clipboard(ChannelInterface $channel)
{
$this->setCurrentResource('channel', $channel);
$this->setClipboard(array(), true);
}
}