HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/brameda/brameda.nl/app/Komma/Shop/Tests/Browser/ShipmentTest.php
<?php

namespace App\Komma\Shop\Tests\Browser;

use App\Komma\Addresses\Models\Address;
use App\Komma\Shop\Cart\ShoppingCartItem;
use App\Komma\Shop\Cart\ShoppingCartService;
use App\Komma\Shop\Checkout\CheckoutService;
use App\Komma\Shop\Orders\Models\Order;
use App\Komma\Shop\Orders\OrderStatus;
use App\Komma\Shop\Products\Product\Product;
use App\Komma\Shop\Products\ProductableInterface;
use App\Komma\Shop\Products\ProductComposite\ProductComposite;
use App\Komma\Shop\Products\ProductGroup\ProductGroup;
use App\Komma\Shop\ShipmentGroups\ShipmentGroup;
use App\Komma\Shop\Shipments\ShipmentService;
use App\Komma\Shop\Shipments\ShipmentStatus;
use App\Komma\Shop\Tests\Browser\Pages\OrdersSectionTestPage;
use App\Komma\Shop\Tests\Browser\Pages\ShipmentGroupsSectionTestPage;
use App\Komma\Shop\Tests\DuskTestCase;
use App\Komma\Users\Models\KmsUser;
use App\Komma\Users\Models\SiteUser;
use Laravel\Dusk\Browser;

class ShipmentTest extends DuskTestCase
{
    /**
     * Test creating shipment via order
     *
     * @group ShipmentTest
     * @test
     * @throws \Throwable
     */
    public function createShipmentViaOrder()
    {
        $self = $this;

        $order = $this->createOrderWithOneOrderedProduct();
        $order->status = OrderStatus::AWAITING_FULFILLMENT;
        $order->save();

        $this->browse(function (Browser $browser) use($order, $self) {
            $browser->loginAs(KmsUser::find(1), 'kms')
                ->screenshot($this->getName(false))
                ->visit(new OrdersSectionTestPage())
                ->assertSee($order->order_number)
                ->click('@order_'.$order->id);
            $self->scrollToElement($browser, '@order_shipments_table');
            $self->assertOrdersShipmentCountIs($browser, 0);

            $browser->click('@create_shipment');
            $self->scrollToElement($browser, '@order_shipments_table');
            $self->assertOrdersShipmentCountIs($browser, 1);
        });
    }

    /**
     * Test creating shipment via Order search / index
     *
     * @group ShipmentTest
     * @test
     * @throws \Throwable
     */
    public function createShipmentOrdersIndex()
    {
        $self = $this;

        $order = $this->createOrderWithOneOrderedProduct();
        $order->status = OrderStatus::AWAITING_FULFILLMENT;
        $order->save();

        $this->browse(function (Browser $browser) use($order, $self) {
            $browser->loginAs(KmsUser::find(1), 'kms')
                ->screenshot($this->getName(false))
                ->visit(new OrdersSectionTestPage())
                ->assertSee($order->order_number); //Belongs to the latest 10 orders so should be visible
            $self->scrollToElement($browser, '@status');
            $browser->select('@status', OrderStatus::AWAITING_FULFILLMENT)
                ->click('@search_orders_button')
                ->assertSee($order->order_number)
                ->assertValue('@status', OrderStatus::AWAITING_FULFILLMENT)
                ->assertVisible('@order_checkbox_'.$order->id)
                ->assertVisible('@create_shipments')
                ->click('@create_shipments')
                ->assertSee(__('shop/orders.batch.no_orders_selected'))
                ->check('@order_checkbox_'.$order->id)
                ->click('@create_shipments')
                ->assertSee(trans_choice('shop/shipments.shipments_created', 1, ['count' => 1]))
                ->assertMissing('@order_'.$order->id) //Missing because status changed
                ->select('@status', OrderStatus::AWAITING_SHIPMENT)
                ->click('@search_orders_button')
                ->assertVisible('@order_'.$order->id); //Missing because status changed

                $self->scrollToElement($browser, '@order_'.$order->id);
                $browser->click('@order_'.$order->id);
                $self->assertOrdersShipmentCountIs($browser, 1);
        });
    }

    /**
     * Test creating shipment group via Order search / index
     *
     * @group ShipmentTest
     * @test
     * @throws \Throwable
     */
    public function createShipmentGroupViaOrdersIndex()
    {
        $self = $this;

        $shipmentService = new ShipmentService();
        $order = $this->createOrderWithOneOrderedProduct();
        $order->status = OrderStatus::AWAITING_SHIPMENT;
        $order->save();
        $shipment = $shipmentService->makeForOrder($order);
        $shipment->save();

        $this->browse(function (Browser $browser) use($order, $shipment, $self) {
            $browser->loginAs(KmsUser::find(1), 'kms')
                ->screenshot($this->getName(false))
                ->visit(new OrdersSectionTestPage())
                ->assertMissing('@group_shipments')
                //Change status to awaiting shipment
                ->select('@status', OrderStatus::AWAITING_SHIPMENT)
                ->click('@search_orders_button');
            $self->scrollToElement($browser, '@group_shipments');
            $browser->click('@group_shipments')
                ->assertSee(__('shop/orders.batch.no_orders_selected'));
            $self->scrollToElement($browser, '@order_checkbox_'.$order->id);
            $browser->check('@order_checkbox_'.$order->id)
                ->click("@group_shipments");

            $latestShipmentGroup = ShipmentGroup::latest()->first();
            $browser->assertSee(trans_choice('shop/shipmentgroups.shipmentGroup_created', 1, ['id' => $latestShipmentGroup->id, 'count' => 1]));
        });
    }

    /**
     * Test creating shipment group via shipment groups search / index
     *
     * @group ShipmentTest
     * @test
     * @throws \Throwable
     */
    public function testCreatingShipmentGroupViaShipmentGroupsIndex()
    {
        $self = $this;

        $shipmentService = new ShipmentService();
        $order = $this->createOrderWithOneOrderedProduct();
        $order->status = OrderStatus::AWAITING_SHIPMENT;
        $order->save();
        $shipment = $shipmentService->makeForOrder($order);
        $shipment->save();

        $latestShipmentGroupBeforeAddingOne = ShipmentGroup::latest()->first();
        $shipmentGroupCountBeforeAddingOne = ShipmentGroup::count();

        $this->browse(function (Browser $browser) use($order, $shipment, $self, $latestShipmentGroupBeforeAddingOne, $shipmentGroupCountBeforeAddingOne) {
            $browser->loginAs(KmsUser::find(1), 'kms')
                ->screenshot($this->getName(false))
                ->visit(new ShipmentGroupsSectionTestPage());

            if($latestShipmentGroupBeforeAddingOne) $browser->assertSee($latestShipmentGroupBeforeAddingOne->id);

            $browser->click('@new_shipment_group');

            $latestShipmentGroup = ShipmentGroup::latest()->first();
            $browser->assertSee($latestShipmentGroup->id);
            $self->assertEquals(($latestShipmentGroupBeforeAddingOne) ? $shipmentGroupCountBeforeAddingOne + 1 : 1, ShipmentGroup::count());
        });
    }

    /**
     * Test creating two shipments for a certain order, put them in a shipment group, and then ship that group completely
     *
     * @group ShipmentTest
     * @test
     * @throws \Throwable
     */
    public function prepareAndShipOrderTest()
    {
        $self = $this;
        $order = $this->createOrderWithOneOrderedProduct(); //We are going to ship this order using 2 shipments and one shipmentgroup
        $order->status = OrderStatus::AWAITING_FULFILLMENT; //This is the status after the order has been payed.
        $order->save();

        $this->browse(function (Browser $browser) use($order, $self) {
            $browser->loginAs(KmsUser::find(1), 'kms')
                ->screenshot($this->getName(false))
                ->visit(new OrdersSectionTestPage());

            //Open the order
            $self->scrollToElement($browser, '@order_checkbox_'.$order->id);
            $browser->click('@order_'.$order->id);

            //Create two shipments
            $self->scrollToElement($browser, 'order_shipments_table');
            $self->assertOrdersShipmentCountIs($browser, 0);
            $browser->click('@create_shipment')
                ->click('@create_shipment');
            $self->assertOrdersShipmentCountIs($browser, 2);

            //Get the orders shipments. So that we can get information from them and reference them in the following assertions
            $shipments = $order->shipments()->get();

            //Generate 2 fake tracking codes
            $firstTrackingCode = str_random();
            $secondTrackingCode = str_random();

            //Open the first shipment and enter a track and trace code, comment, and change the shipment status
            $browser->click('@shipment_'.$shipments[0]->id)
                ->type('@TextField-tracking_code', $firstTrackingCode)
                ->type('@TextArea-comment', 'This shipment can be shipped!');

                //Select the Ready to ship status
                $browser->click('@Select-status')
                ->assertVisible('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->click('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->assertSee(__('shop/shipments.status.'.ShipmentStatus::READY_TO_SHIP))
                ->click('@save_button')
                ->assertSee(__('kms/global.saved'))
                ->click('@Link-order'); //Back to the order

            //Open the second shipment and enter a track and trace code, comment
            $browser->click('@shipment_'.$shipments[1]->id)
                ->type('@TextField-tracking_code', $secondTrackingCode)
                ->type('@TextArea-comment', 'Can be shipped too!');

            //Select the Ready to ship status
            $browser->click('@Select-status')
                ->assertVisible('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->click('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->assertSee(__('shop/shipments.status.'.ShipmentStatus::READY_TO_SHIP))
                ->click('@save_button')
                ->assertSee(__('kms/global.saved'))
                ->click('@Link-order'); //Back to the order

            //Check that we can see the track and trace code and comment of the shipments
            $self->scrollToElement($browser, '@order_shipments_table');
            $browser->assertSee($firstTrackingCode)
            ->assertSee($secondTrackingCode)
            ->assertSee('This shipment can be shipped!')
            ->assertSee('Can be shipped too!')
            ->assertSee(__('shop/shipments.status.'.ShipmentStatus::READY_TO_SHIP));

            //Change the order status to awaiting shipment
            $browser->click('@Select-status')
                ->click('@Select-status-'.OrderStatus::AWAITING_SHIPMENT)
                ->click('@save_button')
                ->assertSee(__('shop/orders.status.'.OrderStatus::AWAITING_SHIPMENT));

            //Create a new shipmentGroup
            $latestShipmentGroupBeforeAddingOne = ShipmentGroup::latest()->first();
            $shipmentGroupCountBeforeAddingOne = ShipmentGroup::count();

            $latestShipmentGroupAfterAddingOne = null;
            $this->browse(function (Browser $browser) use($order, $shipments, $self, $latestShipmentGroupBeforeAddingOne, $shipmentGroupCountBeforeAddingOne, &$latestShipmentGroupAfterAddingOne) {
                $browser->loginAs(KmsUser::find(1), 'kms')
                    ->screenshot($this->getName(false))
                    ->visit(new ShipmentGroupsSectionTestPage())
                    ->assertSee($latestShipmentGroupBeforeAddingOne->id)
                    ->click('@new_shipment_group');

                $latestShipmentGroupAfterAddingOne = ShipmentGroup::latest()->first();
                $browser->assertSee($latestShipmentGroupAfterAddingOne->id);
                $self->assertEquals($shipmentGroupCountBeforeAddingOne + 1, ShipmentGroup::count());
            });

            //Open the shipmentGroup and see if the created shipments can be added.
            $browser->click('@shipment_group_'.$latestShipmentGroupAfterAddingOne->id)
                ->assertSeeIn('@shipments', $firstTrackingCode)
                ->assertSeeIn('@shipments', $secondTrackingCode);

            //Select them and add them to the shipmentgroup
            $self->scrollToElement($browser, '@addable_shipment_'.$shipments[0]->id);
            $browser->check('@addable_shipment_'.$shipments[0]->id)
                ->check('@addable_shipment_'.$shipments[1]->id)
                ->click('@add_to_group')

            //They should not be among the shipments that can be added...
                ->assertDontSeeIn('@shipments', $firstTrackingCode)
                ->assertDontSeeIn('@shipments', $secondTrackingCode)

            //But they should be visible in the added shipments.
                ->assertSeeIn('@shipment_group_shipments', $firstTrackingCode)
                ->assertSeeIn('@shipment_group_shipments', $secondTrackingCode)

            //Lets send the group!
                ->click('@ship')
                ->assertVisible('@confirmBox')
                ->click('@confirmation_confirm');

            //Check that the order is shipped. And that the shipments are shipped.
            $order->refresh();
            $shipments[0]->refresh();
            $shipments[1]->refresh();
            $this->assertTrue($order->status === OrderStatus::SHIPPED);
            $this->assertTrue($shipments[0]->status === ShipmentStatus::IN_TRANSIT);
            $this->assertTrue($shipments[1]->status === ShipmentStatus::IN_TRANSIT);

        });
    }

    /**
     * Test creating two shipments for a certain order, put them in a shipment group, and then ship that group partially.
     * And the completly
     *
     * @group ShipmentTest
     * @test
     * @throws \Throwable
     */
    public function prepareAndShipOrderPartiallyAndThenCompletely()
    {
        $self = $this;
        $order = $this->createOrderWithOneOrderedProduct(); //We are going to ship this order using 2 shipments and one shipmentgroup
        $order->status = OrderStatus::AWAITING_FULFILLMENT; //This is the status after the order has been payed.
        $order->save();

        $this->browse(function (Browser $browser) use($order, $self) {
            $browser->loginAs(KmsUser::find(1), 'kms')
                ->screenshot($this->getName(false))
                ->visit(new OrdersSectionTestPage());

            //Open the order
            $self->scrollToElement($browser, '@order_checkbox_'.$order->id);
            $browser->click('@order_'.$order->id);

            //Create two shipments
            $self->scrollToElement($browser, 'order_shipments_table');
            $self->assertOrdersShipmentCountIs($browser, 0);
            $browser->click('@create_shipment')
                ->click('@create_shipment');
            $self->assertOrdersShipmentCountIs($browser, 2);

            //Get the orders shipments. So that we can get information from them and reference them in the following assertions
            $shipments = $order->shipments()->get();

            //Generate 2 fake tracking codes
            $firstTrackingCode = str_random();
            $secondTrackingCode = str_random();

            //Open the first shipment and enter a track and trace code, comment, and change the shipment status
            $browser->click('@shipment_'.$shipments[0]->id)
                ->type('@TextField-tracking_code', $firstTrackingCode)
                ->type('@TextArea-comment', 'This shipment can be shipped!');

            //Select the Ready to ship status
            $browser->click('@Select-status')
                ->assertVisible('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->click('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->assertSee(__('shop/shipments.status.'.ShipmentStatus::READY_TO_SHIP))
                ->click('@save_button')
                ->assertSee(__('kms/global.saved'))
                ->click('@Link-order'); //Back to the order

            //Open the second shipment and enter a track and trace code, comment
            $browser->click('@shipment_'.$shipments[1]->id)
                ->type('@TextField-tracking_code', $secondTrackingCode)
                ->type('@TextArea-comment', 'Part X is in backorder so we expect this shipment to be shipped in about a week.');

            //Status will be kept on "NEW" since the order was not yet complete
            $browser->assertSee(__('shop/shipments.status.'.ShipmentStatus::NEW))
                ->click('@save_button')
                ->assertSee(__('kms/global.saved'))
                ->click('@Link-order'); //Back to the order

            //Check that we can see the track and trace code and comment of the shipments
            $self->scrollToElement($browser, '@order_shipments_table');
            $browser->assertSee($firstTrackingCode)
                ->assertSee($secondTrackingCode)
                ->assertSee('This shipment can be shipped!')
                ->assertSee('Part X is in backorder so we e')
                ->assertSee(__('shop/shipments.status.'.ShipmentStatus::READY_TO_SHIP))
                ->assertSee(__('shop/shipments.status.'.ShipmentStatus::NEW));

            //The order status still must be on Awaiting fulfilment since it is not yet complete
            $browser->assertSee(__('shop/orders.status.'.OrderStatus::AWAITING_FULFILLMENT));

            //Create a new shipmentGroup
            $latestShipmentGroupBeforeAddingOne = ShipmentGroup::latest()->first();
            $shipmentGroupCountBeforeAddingOne = ShipmentGroup::count();

            $latestShipmentGroupAfterAddingOne = null;
            $this->browse(function (Browser $browser) use($order, $shipments, $self, $latestShipmentGroupBeforeAddingOne, $shipmentGroupCountBeforeAddingOne, &$latestShipmentGroupAfterAddingOne) {
                $browser->loginAs(KmsUser::find(1), 'kms')
                    ->screenshot($this->getName(false))
                    ->visit(new ShipmentGroupsSectionTestPage());
                if($latestShipmentGroupBeforeAddingOne) $browser->assertSee($latestShipmentGroupBeforeAddingOne->id);
                $browser->click('@new_shipment_group');

                $latestShipmentGroupAfterAddingOne = ShipmentGroup::latest()->first();
                $browser->assertSee($latestShipmentGroupAfterAddingOne->id);
                $self->assertEquals($shipmentGroupCountBeforeAddingOne + 1, ShipmentGroup::count());
            });

            //Open the shipmentGroup and see if one of the shipments can be added. And not the other one yet.
            $browser->click('@shipment_group_'.$latestShipmentGroupAfterAddingOne->id)
                ->assertSeeIn('@shipments', $shipments[0]->id);

            //Select the one that is ready to ship and add it to the shipment group
            $self->scrollToElement($browser, '@addable_shipment_'.$shipments[0]->id);
            $browser->check('@addable_shipment_'.$shipments[0]->id)
                ->click('@add_to_group')

                //It should not be among the shipments that can be added...
                ->assertDontSeeIn('@shipments', $firstTrackingCode)

                //But they should be visible in the added shipments.
                ->assertSeeIn('@shipment_group_shipments', $firstTrackingCode)

                //Lets send the group!
                ->click('@ship')
                ->assertVisible('@confirmBox')
                ->click('@confirmation_confirm');

            //Check that the order is partially shipped. This is because though the shipment group did not have the shipment that was new, the order did. And therefore the order is shipped partially.
            //And check that the shipment that was ready to ship, is shipped. The non ready to be shipped one still must be "new"
            $order->refresh();
            $shipments[0]->refresh();
            $shipments[1]->refresh();
            $this->assertEquals(OrderStatus::PARTIALLY_SHIPPED, $order->status);
            $this->assertTrue($shipments[0]->status === ShipmentStatus::IN_TRANSIT);
            $this->assertTrue($shipments[1]->status === ShipmentStatus::NEW);

            //The order now is partially shipped. Next code will ship test that the order completely

            //Now, create a new shipment group in which we are going to finally ship the other shipment.
            $shipmentGroupCountBeforeAddingOne = ShipmentGroup::count();
            $latestShipmentGroupAfterAddingOne = null;
            $this->browse(function (Browser $browser) use($order, $shipments, $self, $latestShipmentGroupBeforeAddingOne, $shipmentGroupCountBeforeAddingOne, &$latestShipmentGroupAfterAddingOne) {
                $browser->loginAs(KmsUser::find(1), 'kms')
                    ->screenshot($this->getName(false))
                    ->visit(new ShipmentGroupsSectionTestPage());
                $browser->click('@new_shipment_group');

                $latestShipmentGroupAfterAddingOne = ShipmentGroup::latest()->first();
                $browser->assertSee($latestShipmentGroupAfterAddingOne->id);
                $self->assertEquals($shipmentGroupCountBeforeAddingOne + 1, ShipmentGroup::count());
            });

            //Open it, add the shipment, and then open the shipment, editing it in a way that it is ready to ship.
            $browser->click('@shipment_group_'.$latestShipmentGroupAfterAddingOne->id)
                ->assertDontSeeIn('@shipments', $shipments[0]->tracking_code) //Already shipped earlier
                ->assertSeeIn('@shipments', $shipments[1]->tracking_code)
                ->check('@addable_shipment_'.$shipments[1]->id)
                ->click('@add_to_group')
                ->assertDontSeeIn('@shipments', $secondTrackingCode)
                ->click('@open_shipment_'.$shipments[1]->id);

            //Select the Ready to ship status and update the comment
            $browser->click('@Select-status')
                ->assertVisible('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->click('@Select-status-'.ShipmentStatus::READY_TO_SHIP)
                ->assertSee(__('shop/shipments.status.'.ShipmentStatus::READY_TO_SHIP))
                ->type('@TextArea-comment', 'The part finally arrived today and we can now ship this order.')
                ->click('@save_button')
                ->assertSee(__('kms/global.saved'))
                ->click('@Link-shipment_group') //Go back to the shipment group

            //Lets send the group!
                ->click('@ship')
                ->assertVisible('@confirmBox')
                ->click('@confirmation_confirm');

            $order->refresh();
            $shipments[0]->refresh();
            $shipments[1]->refresh();
            $this->assertEquals(OrderStatus::SHIPPED, $order->status);
            $this->assertTrue($shipments[0]->status === ShipmentStatus::IN_TRANSIT);
            $this->assertTrue($shipments[1]->status === ShipmentStatus::IN_TRANSIT);
        });
    }


    /**
     * Testing helper method. Scrolls to an element with the given dusk Selector
     *
     * @param Browser $browser
     * @param string $duskSelector
     */
    public function scrollToElement(Browser $browser, $duskSelector)
    {
        $duskSelector = str_replace('@', '', $duskSelector);
        $browser->waitUntil('document.querySelector(\'[dusk="'.$duskSelector.'"]\').scrollIntoView() === undefined;');
    }

    /**
     * Asserts that the amount of shipments in an order is the specified amount. Must be used on the order detail page
     *
     * @param Browser $browser
     * @param int $count
     */
    public function assertShipmentGroupCountIs(Browser $browser, int $count)
    {
        $browser->waitUntil('document.querySelectorAll(\'[dusk^="shipment_group_"]\').length === '.$count);
    }

    /**
     * Asserts that the amount of shipments in an order is the specified amount. Must be used on the order detail page
     *
     * @param Browser $browser
     * @param int $count
     */
    public function assertOrdersShipmentCountIs(Browser $browser, int $count)
    {
        $browser->waitUntil('document.querySelector(\'[dusk="order_shipments_table"]\').childElementCount === '.$count);
    }

    /**
     * Testing helper method.
     *
     * Create an order for a random new user.
     *
     * @return Order
     * @throws \Throwable
     */
    public function createOrderWithOneOrderedProduct(): Order
    {
        //CheckoutService
        $checkoutService = new CheckoutService();

        //ShoppingCart
        $shoppingCart = new ShoppingCartService();

        //Get a random customer user to test with
        /** @var SiteUser $customer */
        $customer = factory(SiteUser::class)->create();
        $this->assertInstanceOf(SiteUser::class, $customer);

        //Give it an address
        /** @var Address $address */
        $address = factory(Address::class)->create();
        $customer->addresses()->save($address);

        //Put some random products in the shopping cart
        $products = Product::inRandomOrder()->take(3)->get();
        $this->assertCount(3, $products);

        //Put some random product groups in the shopping cart
        $productGroups = ProductGroup::inRandomOrder()->take(2)->get();
        $this->assertCount(2, $productGroups);

        //Put some random product composites in the shopping cart
        $productComposites = ProductComposite::inRandomOrder()->take(1)->get();
        $this->assertCount(1, $productComposites);

        //Build an order
        $allProductables = collect();
        $allProductables = $allProductables->merge($products);
        $allProductables = $allProductables->merge($productGroups);
        $allProductables = $allProductables->merge($productComposites);

        $allProductables->each(function(ProductableInterface $productable) use ($shoppingCart) {
            /** @var ShoppingCartItem $shoppingCartItem */
            $shoppingCartItem = $shoppingCart->addProductable($productable, mt_rand(1, 3));
        });

        return $checkoutService->createOrderFromShoppingCartItems($shoppingCart->getItems(), $customer, $customer->addresses()->first(), $customer->addresses()->first());
    }
}