File: D:/HostingSpaces/SBogers10/farmfun.komma.pro/app/Komma/Shop/Tests/Browser/CheckoutTest.php
<?php
namespace App\Komma\Shop\Tests\Browser;
use App\Komma\Addresses\Models\Address;
use App\Komma\Globalization\RegionInfoInterface;
use App\Komma\Kms\QualityAssurance\ExtraDuskTools;
use App\Komma\Shop\Cart\ShoppingCartService;
use App\Komma\Shop\Orders\InvoiceNumberSequence;
use App\Komma\Shop\Orders\Models\Order;
use App\Komma\Shop\Orders\OrderNumberSequence;
use App\Komma\Shop\Orders\OrderStatus;
use App\Komma\Shop\Orders\Product\OrderedProduct;
use App\Komma\Shop\Orders\ProductComposite\OrderedProductComposite;
use App\Komma\Shop\Orders\ProductGroup\OrderedProductGroup;
use App\Komma\Shop\Payment\TransactionStatus;
use App\Komma\Shop\Products\Product\Product;
use App\Komma\Shop\Products\ProductComposite\ProductComposite;
use App\Komma\Shop\Products\ProductGroup\ProductGroup;
use App\Komma\Shop\Tests\Browser\Pages\ShoppingCartTestPage;
use App\Komma\Shop\Tests\DuskTestCase;
use App\Komma\Users\Models\SiteUser;
use Illuminate\Database\Eloquent\Collection;
use Laravel\Dusk\Browser;
class CheckoutTest extends DuskTestCase
{
// use DatabaseTransactions; //Automatically rolls back database actions after tests. Note: when you enable this, assertProductVisible creates a user but cannot be logged in somehow. Laravel dusk problem i think.
/**
* @group Checkout
* @test
* @throws \Throwable
*/
public function assertCartEmpty()
{
$shoppingCartService = new ShoppingCartService();
$shoppingCartService->clear(); //Clear the cart if it not already was
$regionInfo = app(RegionInfoInterface::class);
$counter = 0;
$this->browse(function (Browser $browser) use ($regionInfo) {
$browser->loginAs(SiteUser::find(1), 'site')
->visit(new ShoppingCartTestPage())
->assertSee(__('shop/cart.cart'))
->assertSeeIn('@subtotal', $regionInfo->getCurrencySymbol().' 0');
});
}
/**
* Test coupon codes in checkout as authenticated user
*
* @group Checkout
* @test
* @throws \Throwable
*/
public function testCouponAndVat()
{
/** @var SiteUser $user */
$user = factory(SiteUser::class)->create();
/** @var Address $address */
$address = factory(Address::class)->create();
$user->addresses()->save($address);
$this->assertDatabaseHas('site_users', [
'id' => $user->id,
]);
$product = Product::find(1);
$regionInfo = app(RegionInfoInterface::class);
$this->browse(function (Browser $browser) use ($user, $product, $address, $regionInfo) {
$browser->logout('site'); //Make sure we dont have a session
$browser->loginAs($user->id, 'site')
->visit(new ShoppingCartTestPage())
->click('@addProductWithId1')
->waitUntil('document.querySelector(\'[dusk="shoppingCartItemList"]\').childElementCount == 1')
->click('@checkout')
->assertSee(__('shop/checkout.invoice_address'))
->assertSee(__('shop/checkout.shipping_address'))
->scrollToElement('[dusk="checkout"]') //See extra dusk tools
->assertVisible('[dusk="checkout"]')
->assertSee(__('shop/checkout.agree_to_terms'))
->type('@coupon_code', 'Absolute5') //Add 5 euro discount
->click('@add_coupon')
->pause(100)
->scrollToElement('[dusk="Absolute5"]') //See extra dusk tools
->assertVisible('[dusk="Absolute5"]')
->select('shipping_country', 'GBR')->pause(150)
//The product is added 10 times thanks to cart.js addProductToShoppingcart method. 500 is 500 cents from Absolute5 coupon. And 1499 is the shipping costs of GBR
->assertSee($regionInfo->getNumberFormat()->centsToCurrency($product->getTotal() * 10))
->assertSee($regionInfo->getNumberFormat()->centsToCurrency(round((((($product->getTotal() * 10) + 1499) - 500) * 0.21))));
});
}
/**
* @group Checkout
* @test
* @see ExtraDuskTools
* @throws \Throwable
*/
public function assertCartNotEmptyWhenRandomProductAdded()
{
$shoppingCartService = new ShoppingCartService();
$shoppingCartService->clear(); //Clear the cart if it not already was
$regionInfo = app(RegionInfoInterface::class);
$this->browse(function (Browser $browser) use ($regionInfo, $shoppingCartService) {
$browser->loginAs(SiteUser::find(1), 'site');
$browser->visit(new ShoppingCartTestPage())
->assertSee(__('shop/cart.cart'))
->pause(500)
->assertSeeIn('@subtotal', $regionInfo->getCurrencySymbol().' 0')
->waitFor('@addRandomProduct')
->waitUntil('document.readyState === "complete"')->pause(500) //Also wait till javascript is loaded completely.
->click('@addRandomProduct')
->waitUntil('document.querySelectorAll(\'[dusk="shoppingCartItemList"] > li\').length === 1') //Wait until one item is visible in the list of shopping cart items
->assertDontSeeIn('@subtotal', $regionInfo->getCurrencySymbol().' 0');
});
}
/**
* Test a successful checkout with payment as authenticated user
*
* @group Checkout
* @test
* @throws \Throwable
*/
public function testInvalidCheckoutForm()
{
/** @var SiteUser $user */
$user = factory(SiteUser::class)->create();
/** @var Address $address */
$address = factory(Address::class)->create();
$user->addresses()->save($address);
$this->assertDatabaseHas('site_users', [
'id' => $user->id,
]);
$regionInfo = app(RegionInfoInterface::class);
$product = Product::find(1);
$this->browse(function (Browser $browser) use ($user, $product, $regionInfo, $address) {
$browser->loginAs($user->id, 'site')
->visit(new ShoppingCartTestPage())
->click('@addProductWithId1')
->waitForReload()
->click('@checkout')
->assertSee(__('shop/checkout.invoice_address'))
->assertSee(__('shop/checkout.shipping_address'))
->scrollToElement('[dusk="checkout"]') //See extra dusk tools
->assertVisible('[dusk="checkout"]')
->assertSee(__('shop/checkout.agree_to_terms'))
->assertRadioSelected('@new_invoice_address', '-1') //-1 Means that the backend could see that this is a new address instead of an existing one (bigger as 0 value);
->assertRadioSelected('@new_shipping_address', '-1')
->pause(10)
->assertSee($product->getDisplayName());
$browser->driver->executeScript('window.scrollTo(0,document.body.scrollHeight);'); //Scroll to bottom
$browser->pause(10)
->click('@checkout')
->pause(250);
foreach (['shipping', 'invoice'] as $type) {
foreach ([
$type.'_street',
$type.'_house_number',
$type.'_postal_code',
$type.'_city',
$type.'_phone',
] as $attribute) {
$browser->assertSee(__('shop/checkout.validation.required_if', ['attribute' => __('validation.attributes.'.$attribute)]));
}
}
});
}
/**
* Test a successful checkout with payment as authenticated user
*
* @group Checkout
* @test
* @throws \Throwable
*/
public function testCheckoutForm()
{
/** @var SiteUser $user */
$user = factory(SiteUser::class)->create();
/** @var Address $address */
$address = factory(Address::class)->create();
$user->addresses()->save($address);
$this->assertDatabaseHas('site_users', [
'id' => $user->id,
]);
$regionInfo = app(RegionInfoInterface::class);
$product = Product::find(1);
$this->browse(function (Browser $browser) use ($user, $product, $regionInfo, $address) {
$browser->loginAs($user->id, 'site')
->visit(new ShoppingCartTestPage())
->click('@addProductWithId1')
->waitForReload()
->click('@checkout')
->assertSee(__('shop/checkout.invoice_address'))
->assertSee(__('shop/checkout.shipping_address'))
->assertMissing('first_name') //Only visible for guests
->assertMissing('last_name') //Only visible for guests
->assertMissing('email') //Only visible for guests
->assertMissing('country') //Only visible for guests
->assertMissing('language') //Only visible for guests
->scrollToElement('[dusk="checkout"]') //See extra dusk tools
->assertVisible('[dusk="checkout"]')
->assertSee(__('shop/checkout.agree_to_terms'))
->assertRadioSelected('@new_invoice_address', '-1') //-1 Means that the backend could see that this is a new address instead of an existing one (bigger as 0 value);
->assertRadioSelected('@new_shipping_address', '-1')
->type('@invoice_street', $address->street)
->type('@invoice_house_number', $address->house_number)
->type('@invoice_postal_code', $address->postal_code)
->type('@invoice_city', $address->city)
->type('@invoice_phone', $address->telephone)
->select('@invoice_country', $address->country_iso3)
->type('@shipping_street', $address->street)
->type('@shipping_house_number', $address->house_number)
->type('@shipping_postal_code', $address->postal_code)
->type('@shipping_city', $address->city)
->type('@shipping_phone', $address->telephone)
->select('@shipping_country', $address->country_iso3)
->type('@remarks', 'None')
->assertInputValue('@invoice_street', $address->street)
->assertInputValue('@invoice_house_number', $address->house_number)
->assertInputValue('@invoice_postal_code', $address->postal_code)
->assertInputValue('@invoice_city', $address->city)
->assertInputValue('@invoice_phone', $address->telephone)
->assertSelected('@invoice_country', $address->country_iso3)
->assertInputValue('@shipping_street', $address->street)
->assertInputValue('@shipping_house_number', $address->house_number)
->assertInputValue('@shipping_postal_code', $address->postal_code)
->assertInputValue('@shipping_city', $address->city)
->assertInputValue('@shipping_phone', $address->telephone)
->assertSelected('@shipping_country', $address->country_iso3)
->assertInputValue('@remarks', 'None')
->assertSee($product->getDisplayName());
$browser->driver->executeScript('window.scrollTo(0,document.body.scrollHeight);'); //Scroll to bottom
$browser
->check('@terms_and_conditions')
->click('@checkout')
->pause(500)
->assertSee('Welkom bij KommaPSP')
->select('@payment_status', TransactionStatus::PAYMENT_PAID)
->click('@redir') //Pay
->assertSee('We have received your payment')
->assertSee(TransactionStatus::PAYMENT_PAID)
->assertSee(OrderStatus::AWAITING_FULFILLMENT)
->assertMissing('@register'); //User is no guest so the register incentive should not be visible.
});
}
/**
* Test a successful guest checkout for a guest that orders for the first time
*
* @group Checkout
* @test
* @throws \Throwable
*/
public function testCheckoutFormAsFirstTimeGuest()
{
/** @var SiteUser $user */
$user = factory(SiteUser::class)->make(); //DO NOT SAVE IT!
/** @var Address $address */
$address = factory(Address::class)->make(); //DO NOT SAVE IT!
$regionInfo = app(RegionInfoInterface::class);
$product = Product::find(1);
$this->assertDatabaseMissing($user->getTable(), [
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => $user->email,
'culture' => 'fy-NL',
'guest' => 1,
]);
$this->browse(function (Browser $browser) use ($user, $product, $regionInfo, $address) {
$browser->visit(new ShoppingCartTestPage())
->click('@addProductWithId1')
->waitForReload()
->click('@checkout')
->assertVisible('@guest_checkout')
->assertVisible('@account_checkout')
->click('@guest_checkout')
->assertVisible('@first_name') //Only visible for guests
->assertVisible('@last_name') //Only visible for guests
->assertVisible('@email') //Only visible for guests
->assertVisible('@country') //Only visible for guests
->assertVisible('@language') //Only visible for guests
->type('@first_name', $user->first_name)
->type('@last_name', $user->last_name)
->type('@email', $user->email)
->select('country', 'NLD')
->pause(150)
->select('language', 'fry')
->assertSee(__('shop/checkout.invoice_address'))
->assertSee(__('shop/checkout.shipping_address'))
->scrollToElement('[dusk="checkout"]') //See extra dusk tools
->assertVisible('[dusk="checkout"]')
->assertSee(__('shop/checkout.agree_to_terms'))
->assertMissing('@new_invoice_address')
->assertMissing('@new_shipping_address')
->type('@invoice_street', $address->street)
->type('@invoice_house_number', $address->house_number)
->type('@invoice_postal_code', $address->postal_code)
->type('@invoice_city', $address->city)
->type('@invoice_phone', $address->telephone)
->select('@invoice_country', $address->country_iso3)
->type('@shipping_street', $address->street)
->type('@shipping_house_number', $address->house_number)
->type('@shipping_postal_code', $address->postal_code)
->type('@shipping_city', $address->city)
->type('@shipping_phone', $address->telephone)
->select('@shipping_country', $address->country_iso3)
->type('@remarks', 'None')
->assertInputValue('@invoice_street', $address->street)
->assertInputValue('@invoice_house_number', $address->house_number)
->assertInputValue('@invoice_postal_code', $address->postal_code)
->assertInputValue('@invoice_city', $address->city)
->assertInputValue('@invoice_phone', $address->telephone)
->assertSelected('@invoice_country', $address->country_iso3)
->assertInputValue('@shipping_street', $address->street)
->assertInputValue('@shipping_house_number', $address->house_number)
->assertInputValue('@shipping_postal_code', $address->postal_code)
->assertInputValue('@shipping_city', $address->city)
->assertInputValue('@shipping_phone', $address->telephone)
->assertSelected('@shipping_country', $address->country_iso3)
->assertInputValue('@remarks', 'None')
->assertSee($product->getDisplayName());
$browser->driver->executeScript('window.scrollTo(0,document.body.scrollHeight);'); //Scroll to bottom
$browser->check('@terms_and_conditions')
->click('@checkout')
->pause(500)
->assertSee('Welkom bij KommaPSP')
->select('@payment_status', TransactionStatus::PAYMENT_PAID)
->click('@redir') //Pay
->assertSee('We have received your payment')
->assertSee(TransactionStatus::PAYMENT_PAID)
->assertSee(OrderStatus::AWAITING_FULFILLMENT)
->assertVisible('@register'); //Part of an incentive the let the user create an account
});
$this->assertDatabaseHas($user->getTable(), [
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => $user->email,
'culture' => 'fy-NL',
'guest' => 1,
]);
}
/**
* Test a successful guest checkout for a guest that orders for the first time
*
* @group Checkout
* @test
* @throws \Throwable
*/
public function testCheckoutFormAsSecondTimeGuest()
{
//Create a guest user in the database
/** @var SiteUser $originalUser */
$originalUser = factory(SiteUser::class)->create([
'culture' => 'fy-NL',
'guest' => 1,
]);
//Make an address that he can enter
/** @var Address $address */
$address = factory(Address::class)->make();
//Check that the customer exists in the database
$this->assertEquals(1,
SiteUser::where('first_name', '=', $originalUser->first_name)
->where('last_name', '=', $originalUser->last_name)
->where('email', '=', $originalUser->email)
->where('guest', '=', 1)->where('culture', '=', 'fy-NL')->count()
);
//Create an order for the customer
/** @var OrderNumberSequence $orderNumberGenerator */
$orderNumberGenerator = app(OrderNumberSequence::class);
$invoiceNumberGenerator = app(InvoiceNumberSequence::class);
/** @var Order $order */
$order = factory(Order::class)->create();
$order->customer()->associate($originalUser);
$order->order_number = (string) $orderNumberGenerator->next();
$order->invoice_number = (string) $invoiceNumberGenerator->next();
$order->save();
//Validate that the customer has the order
$this->assertEquals(1, $originalUser->orders()->count());
//Get the product that the user should order
$product = Product::find(1);
//Use the browser to order that product, and create a second order.
$this->browse(function (Browser $browser) use ($originalUser, $product, $address) {
$browser->visit(new ShoppingCartTestPage())
->click('@addProductWithId1')
->waitForReload()
->click('@checkout')
->assertVisible('@guest_checkout')
->assertVisible('@account_checkout')
->click('@guest_checkout')
->assertVisible('@first_name') //Only visible for guests
->assertVisible('@last_name') //Only visible for guests
->assertVisible('@email') //Only visible for guests
->assertVisible('@country') //Only visible for guests
->assertVisible('@language') //Only visible for guests
->type('@first_name', $originalUser->first_name)
->type('@last_name', $originalUser->last_name)
->type('@email', $originalUser->email)
->select('country', 'NLD')
->pause(150)
->select('language', 'nld') //Change the language
->assertSee(__('shop/checkout.invoice_address'))
->assertSee(__('shop/checkout.shipping_address'))
->scrollToElement('[dusk="checkout"]') //See extra dusk tools
->assertVisible('[dusk="checkout"]')
->assertSee(__('shop/checkout.agree_to_terms'))
->assertMissing('@new_invoice_address') //Important check! Although the guest user already has an address, we dont present him his existing addresses, simply because he isn't authenticated and he COULD be someone else, knowing the e-mail adres of someone else.
->assertMissing('@new_shipping_address') //Same as previous line.
->type('@invoice_street', $address->street)
->type('@invoice_house_number', $address->house_number)
->type('@invoice_postal_code', $address->postal_code)
->type('@invoice_city', $address->city)
->type('@invoice_phone', $address->telephone)
->select('@invoice_country', $address->country_iso3)
->type('@shipping_street', $address->street)
->type('@shipping_house_number', $address->house_number)
->type('@shipping_postal_code', $address->postal_code)
->type('@shipping_city', $address->city)
->type('@shipping_phone', $address->telephone)
->select('@shipping_country', $address->country_iso3)
->type('@remarks', 'None')
->assertInputValue('@invoice_street', $address->street)
->assertInputValue('@invoice_house_number', $address->house_number)
->assertInputValue('@invoice_postal_code', $address->postal_code)
->assertInputValue('@invoice_city', $address->city)
->assertInputValue('@invoice_phone', $address->telephone)
->assertSelected('@invoice_country', $address->country_iso3)
->assertInputValue('@shipping_street', $address->street)
->assertInputValue('@shipping_house_number', $address->house_number)
->assertInputValue('@shipping_postal_code', $address->postal_code)
->assertInputValue('@shipping_city', $address->city)
->assertInputValue('@shipping_phone', $address->telephone)
->assertSelected('@shipping_country', $address->country_iso3)
->assertInputValue('@remarks', 'None')
->assertSee($product->getDisplayName());
$browser->driver->executeScript('window.scrollTo(0,document.body.scrollHeight);'); //Scroll to bottom
$browser->check('@terms_and_conditions')
->click('@checkout')
->pause(500)
->assertSee('Welkom bij KommaPSP')
->select('@payment_status', TransactionStatus::PAYMENT_PAID)
->click('@redir') //Pay
->assertSee('We have received your payment')
->assertSee(TransactionStatus::PAYMENT_PAID)
->assertSee(OrderStatus::AWAITING_FULFILLMENT)
->assertVisible('@register'); //Part of an incentive the let the user create an account
});
//Get all the users with the same data as the original user. This should be a collection of 1 user.
/** @var Collection $siteUsers */
$siteUsers = SiteUser::where('first_name', '=', $originalUser->first_name)
->where('last_name', '=', $originalUser->last_name)
->where('email', '=', $originalUser->email)
->where('guest', '=', 1)->where('culture', '=', 'nl-NL')->get(); //Notice, the culture has changed, because the user did choose another language.
/** @var SiteUser $siteUser */
$siteUser = $siteUsers->first();
$this->assertEquals(1, $siteUsers->count(), 'There should still be one user with email address: '.$originalUser->email.'. But this seems to be not the case.'); //Still one user.
$this->assertEquals(2, $siteUser->orders()->count()); //But with 2 orders.
}
/**
* Test thanks for order mail contents
*
* @group Checkout
* @test
* @throws \Throwable
*/
public function testThanksForOrderMailContents()
{
$orderStatus = OrderStatus::PENDING;
/** @var Order $order */
$order = Order::inRandomOrder()->first();
$orderedProductComposites = $order->orderedProductComposites()->get();
$orderedProductGroups = $order->orderedGroups()->get();
$orderedProducts = $order->orderedProducts()->get();
$self = $this;
$route = route('preview.mail.order_status', ['status' => $orderStatus, 'staffOrCustomer' => 'customer', 'order' => $order->id]); //Thanks for your order mail
$this->browse(function (Browser $browser) use ($route, $orderStatus, $order, $orderedProducts, $orderedProductGroups, $orderedProductComposites, $self) {
$browser->visit($route)
//Assert general texts
->assertSee(__('shop/orders.mail.'.$orderStatus.'.customer.body'))
->assertSee(__('shop/orders.mail.'.$orderStatus.'.customer.button_text'))
->assertSee(__('shop/orders.mail.'.$orderStatus.'.customer.closure'))
->assertSee(config('site.company.name'));
$self->assertProductableNamesVisible($browser, $orderedProducts);
$self->assertProductableNamesVisible($browser, $orderedProductGroups);
$self->assertProductableNamesVisible($browser, $orderedProductComposites);
});
}
/**
* Test the contents of the mail that is sent to the customer and staff
* when he completed the payment
*
* @group Checkout
* @test
* @throws \Throwable
*/
public function testCustomerPaidMailToCustomerAndStaff()
{
$orderStatus = OrderStatus::AWAITING_FULFILLMENT;
/** @var Order $order */
$order = Order::inRandomOrder()->first();
$orderedProductComposites = $order->orderedProductComposites()->get();
$orderedProductGroups = $order->orderedGroups()->get();
$orderedProducts = $order->orderedProducts()->get();
$self = $this;
foreach (['customer', 'staff'] as $type) {
$route = route('preview.mail.order_status', ['status' => $orderStatus, 'staffOrCustomer' => $type, 'order' => $order->id]); //Thanks for your order mail
$this->browse(function (Browser $browser) use ($route, $orderStatus, $type, $order, $orderedProducts, $orderedProductGroups, $orderedProductComposites, $self) {
$browser->visit($route)
//Assert general text
->assertSee(__('shop/orders.mail.'.$orderStatus.'.'.$type.'.body'))
->assertSee(__('shop/orders.mail.'.$orderStatus.'.'.$type.'.button_text'))
->assertSee(__('shop/orders.mail.'.$orderStatus.'.'.$type.'.closure'))
->assertSee(config('site.company.name'));
$self->assertProductableNamesVisible($browser, $orderedProducts);
$self->assertProductableNamesVisible($browser, $orderedProductGroups);
$self->assertProductableNamesVisible($browser, $orderedProductComposites);
});
}
}
/**
* Helper function that tests if it can see a productable
*
* @param Browser $browser
* @param Collection $orderedProductableCollection
*/
private function assertProductableNamesVisible(Browser $browser, Collection $orderedProductableCollection)
{
foreach ($orderedProductableCollection as $orderedProductable) {
$name = '';
switch (get_class($orderedProductable)) {
case OrderedProduct::class:
/** @var OrderedProduct $orderedProductable */
$product = $orderedProductable->product()->first();
/** @var Product $name */
$name = $product->getDisplayName();
break;
case OrderedProductGroup::class:
/** @var OrderedProductGroup $orderedProductable */
$productGroup = $orderedProductable->productGroup()->first();
/** @var ProductGroup $productGroup */
$name = $productGroup->getDisplayName();
break;
case OrderedProductComposite::class:
/** @var OrderedProductComposite $orderedProductable */
$orderedProductable = $orderedProductable->productComposite()->first();
/** @var ProductComposite $orderedProductable */
$name = $orderedProductable->getDisplayName();
break;
default:
return;
}
if ($name != '') {
$browser->assertSee($name);
}
}
}
}