From 162b93d50b7d3145c89a2097f126f0ded0c71e39 Mon Sep 17 00:00:00 2001 From: Mattias Persson Date: Thu, 22 Aug 2019 16:54:47 +0200 Subject: [PATCH] Updated the cart controllers to use the new API #10 --- src/Controllers/CartController.php | 32 +++-- src/Controllers/CartItemController.php | 60 ++++++--- tests/Feature/Cart/AddCartItemTest.php | 146 --------------------- tests/Feature/Cart/CartControllerTest.php | 51 ------- tests/Feature/Cart/RemoveCartItemTest.php | 88 ------------- tests/Feature/Cart/UpdateCartItemTest.php | 112 ---------------- tests/Http/Cart/AddCartItemHttpTest.php | 101 ++++++++++++++ tests/Http/Cart/ClearCartHttpTest.php | 20 +++ tests/Http/Cart/GetCartCountHttpTest.php | 19 +++ tests/Http/Cart/GetCartHttpTest.php | 19 +++ tests/Http/Cart/RemoveCartItemHttpTest.php | 24 ++++ tests/Http/Cart/UpdateCartItemHttpTest.php | 25 ++++ 12 files changed, 267 insertions(+), 430 deletions(-) delete mode 100644 tests/Feature/Cart/AddCartItemTest.php delete mode 100644 tests/Feature/Cart/CartControllerTest.php delete mode 100644 tests/Feature/Cart/RemoveCartItemTest.php delete mode 100644 tests/Feature/Cart/UpdateCartItemTest.php create mode 100644 tests/Http/Cart/AddCartItemHttpTest.php create mode 100644 tests/Http/Cart/ClearCartHttpTest.php create mode 100644 tests/Http/Cart/GetCartCountHttpTest.php create mode 100644 tests/Http/Cart/GetCartHttpTest.php create mode 100644 tests/Http/Cart/RemoveCartItemHttpTest.php create mode 100644 tests/Http/Cart/UpdateCartItemHttpTest.php diff --git a/src/Controllers/CartController.php b/src/Controllers/CartController.php index 455783c..f819e84 100644 --- a/src/Controllers/CartController.php +++ b/src/Controllers/CartController.php @@ -2,32 +2,40 @@ namespace Happypixels\Shopr\Controllers; -use Happypixels\Shopr\Cart\Cart; use Illuminate\Routing\Controller; +use Happypixels\Shopr\Facades\Cart; class CartController extends Controller { - protected $cart; - - public function __construct(Cart $cart) - { - $this->cart = $cart; - } - + /** + * Returns the full cart summary. + * + * @return \Illuminate\Http\JsonResponse + */ public function index() { - return $this->cart->summary(); + return Cart::get(); } + /** + * Returns the count of the cart. + * + * @return \Illuminate\Http\JsonResponse + */ public function count() { - return ['count' => $this->cart->count()]; + return ['count' => Cart::count()]; } + /** + * Clears the cart and returns the full cart summary. + * + * @return \Illuminate\Http\JsonResponse + */ public function destroy() { - $this->cart->clear(); + Cart::clear(); - return $this->cart->summary(); + return Cart::get(); } } diff --git a/src/Controllers/CartItemController.php b/src/Controllers/CartItemController.php index af9792e..94b5559 100644 --- a/src/Controllers/CartItemController.php +++ b/src/Controllers/CartItemController.php @@ -3,8 +3,8 @@ namespace Happypixels\Shopr\Controllers; use Illuminate\Http\Request; -use Happypixels\Shopr\Cart\Cart; use Illuminate\Routing\Controller; +use Happypixels\Shopr\Facades\Cart; use Happypixels\Shopr\Rules\Discounts\NotADiscount; use Illuminate\Foundation\Validation\ValidatesRequests; @@ -12,13 +12,12 @@ class CartItemController extends Controller { use ValidatesRequests; - protected $cart; - - public function __construct(Cart $cart) - { - $this->cart = $cart; - } - + /** + * Adds an item to the cart. Returns the full cart summary. + * + * @param Request $request + * @return \Illuminate\Http\JsonResponse + */ public function store(Request $request) { $this->validate($request, [ @@ -26,29 +25,48 @@ public function store(Request $request) 'shoppable_id' => 'required', ]); - $item = $this->cart->addItem( - $request->shoppable_type, - $request->shoppable_id, - $request->get('quantity', 1), - $request->get('options', []), - $request->get('sub_items', []), - $request->get('price', null) - ); + $shoppable = $request->shoppable_type::findOrFail($request->shoppable_id); + + $subItems = collect($request->get('sub_items', []))->map(function ($subItem) { + $subItem['shoppable'] = $subItem['shoppable_type']::findOrFail($subItem['shoppable_id']); + + return $subItem; + })->toArray(); + + Cart::add($shoppable, [ + 'quantity' => $request->get('quantity', 1), + 'options' => $request->get('options', null), + 'sub_items' => $subItems, + 'price' => $request->get('price', null), + ]); - return $this->cart->summary(); + return Cart::get(); } + /** + * Updates a cart item. Returns the full cart summary. + * + * @param Request $request + * @param string $id + * @return \Illuminate\Http\JsonResponse + */ public function update(Request $request, $id) { - $this->cart->updateItem($id, $request->all()); + Cart::update($id, $request->all()); - return $this->cart->summary(); + return Cart::get(); } + /** + * Removes an item from the cart and returns the full cart summary. + * + * @param string $id + * @return \Illuminate\Http\JsonResponse + */ public function destroy($id) { - $this->cart->removeItem($id); + Cart::delete($id); - return $this->cart->summary(); + return Cart::get(); } } diff --git a/tests/Feature/Cart/AddCartItemTest.php b/tests/Feature/Cart/AddCartItemTest.php deleted file mode 100644 index d3f77a1..0000000 --- a/tests/Feature/Cart/AddCartItemTest.php +++ /dev/null @@ -1,146 +0,0 @@ -json('POST', 'api/shopr/cart/items') - ->assertStatus(422) - ->assertJsonValidationErrors(['shoppable_type', 'shoppable_id']); - } - - /** @test */ - public function discounts_are_not_allowed() - { - $discount = factory(DiscountCoupon::class)->create(); - - $this->json('POST', 'api/shopr/cart/items', [ - 'shoppable_type' => get_class($discount), - 'shoppable_id' => $discount->id, - 'quantity' => 1, - ]) - ->assertStatus(422) - ->assertJsonValidationErrors(['shoppable_type']) - ->assertJsonFragment(['Invalid shoppable.']); - } - - /** @test */ - public function it_throws_404_error_if_shoppable_is_not_found() - { - $this->json('POST', 'api/shopr/cart/items', [ - 'shoppable_id' => 2, - 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', - ]) - ->assertStatus(404); - } - - /** @test */ - public function it_adds_an_item() - { - $cart = app(Cart::class); - - $this->assertEquals(0, $cart->count()); - - $this->json('POST', 'api/shopr/cart/items', [ - 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', - 'shoppable_id' => 1, - 'quantity' => 1, - ])->assertStatus(200); - - $this->assertEquals(1, $cart->count()); - } - - /** @test */ - public function adding_an_item_returns_cart_summary() - { - $cart = app(Cart::class); - - $this->json('POST', 'api/shopr/cart/items', [ - 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', - 'shoppable_id' => 1, - 'quantity' => 2, - ])->assertJsonFragment([ - 'count' => $cart->count(), - 'total' => $cart->total(), - 'sub_total' => $cart->subTotal(), - 'tax_total' => $cart->taxTotal(), - ])->assertJsonStructure(['count', 'total', 'sub_total', 'tax_total', 'items']); - } - - /** @test */ - public function it_adds_provided_quantity() - { - $cart = app(Cart::class); - - $this->assertEquals(0, $cart->count()); - - $this->json('POST', 'api/shopr/cart/items', [ - 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', - 'shoppable_id' => 1, - 'quantity' => 3, - ]); - - $this->assertEquals(3, $cart->count()); - } - - /** @test */ - public function it_defaults_to_1_if_no_quantity_is_given() - { - $cart = app(Cart::class); - - $this->assertEquals(0, $cart->count()); - - $this->json('POST', 'api/shopr/cart/items', [ - 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', - 'shoppable_id' => 1, - ]); - - $this->assertEquals(1, $cart->count()); - } - - /** @test */ - public function it_allows_price_overrides() - { - $cart = app(Cart::class); - - $this->json('POST', 'api/shopr/cart/items', [ - 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', - 'shoppable_id' => 1, - 'price' => 2018.50, - ]); - - $items = $cart->items(); - - $this->assertEquals(2018.50, $items->first()->price); - $this->assertEquals(2018.50, $cart->total()); - } - - /** @test */ - public function it_adds_subitems() - { - $cart = app(Cart::class); - - $data = [ - 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', - 'shoppable_id' => 1, - 'quantity' => 2, - 'price' => 50, - ]; - - // Add an identical sub item. - $data['sub_items'] = [$data]; - - $response = $this->json('POST', 'api/shopr/cart/items', $data)->assertStatus(200); - - $subItems = $cart->items()->first()->subItems; - $this->assertEquals(1, $subItems->count()); - $this->assertEquals(50, $subItems->first()->price); - } -} diff --git a/tests/Feature/Cart/CartControllerTest.php b/tests/Feature/Cart/CartControllerTest.php deleted file mode 100644 index 21b0eee..0000000 --- a/tests/Feature/Cart/CartControllerTest.php +++ /dev/null @@ -1,51 +0,0 @@ -json('GET', 'api/shopr/cart') - ->assertStatus(200) - ->assertJsonFragment($cart->summary()); - } - - /** @test */ - public function cart_count() - { - $cart = app(Cart::class); - $model = TestShoppable::first(); - $cart->addItem(get_class($model), $model->id, 2); - - $this->json('GET', 'api/shopr/cart/count') - ->assertStatus(200) - ->assertJsonFragment(['count' => 2]); - } - - /** @test */ - public function it_clears_the_cart() - { - $cart = app(Cart::class); - $model = TestShoppable::first(); - $cart->addItem(get_class($model), $model->id, 1); - - Event::fake(); - - $this->json('DELETE', 'api/shopr/cart') - ->assertStatus(200) - ->assertJsonFragment(['count' => 0]); - - $this->assertEquals(0, $cart->items()->count()); - - Event::assertDispatched('shopr.cart.cleared'); - } -} diff --git a/tests/Feature/Cart/RemoveCartItemTest.php b/tests/Feature/Cart/RemoveCartItemTest.php deleted file mode 100644 index d60f588..0000000 --- a/tests/Feature/Cart/RemoveCartItemTest.php +++ /dev/null @@ -1,88 +0,0 @@ -addItem(get_class($model), 1, 1, $options = []); - $item2 = $cart->addItem(get_class($model), 1, 1, $options = ['color' => 'Green']); - - $this->assertEquals(2, $cart->count()); - - $this->json('DELETE', 'api/shopr/cart/items/'.$item2->id) - ->assertStatus(200) - ->assertJsonFragment(['count' => 1]); - - $this->assertEquals(1, $cart->count()); - $this->assertEquals($item->id, $cart->items()->first()->id); - } - - /** @test */ - public function it_does_not_remove_discount_coupons() - { - $discount = factory(DiscountCoupon::class)->create(); - $cart = app(Cart::class); - $model = TestShoppable::first(); - $item = $cart->addItem(get_class($model), $model->id, 1); - $item2 = $cart->addItem(get_class($model), $model->id, 1, $options = ['size' => 'L']); - - $cart->addDiscount($discount); - - $this->json('DELETE', 'api/shopr/cart/items/'.$item2->id) - ->assertStatus(200) - ->assertJsonFragment(['count' => 1]); - - $this->assertTrue($cart->hasDiscount($discount->code)); - } - - /** @test */ - public function removing_the_last_item_also_removes_all_discount_coupons() - { - $discount = factory(DiscountCoupon::class)->create(); - $cart = app(Cart::class); - $model = TestShoppable::first(); - - $item = $cart->addItem(get_class($model), $model->id, 1); - - $cart->addDiscount($discount); - - $this->json('DELETE', 'api/shopr/cart/items/'.$item->id) - ->assertStatus(200) - ->assertJsonFragment(['count' => 0]); - - $summary = $cart->summary(); - $this->assertEquals(0, $summary['items']->count()); - $this->assertEquals(0, $summary['discounts']->count()); - } - - /** @test */ - public function it_fires_event() - { - $cart = app(Cart::class); - $model = TestShoppable::first(); - $item = $cart->addItem(get_class($model), 1, 1, $options = []); - - Event::fake(); - - $this->json('DELETE', 'api/shopr/cart/items/'.$item->id) - ->assertStatus(200) - ->assertJsonFragment(['count' => 0]); - - Event::assertDispatched('shopr.cart.items.deleted', function ($event, $data) use ($item) { - return - $item->id === $data->id && - serialize($item) === serialize($data); - }); - } -} diff --git a/tests/Feature/Cart/UpdateCartItemTest.php b/tests/Feature/Cart/UpdateCartItemTest.php deleted file mode 100644 index 641a84d..0000000 --- a/tests/Feature/Cart/UpdateCartItemTest.php +++ /dev/null @@ -1,112 +0,0 @@ -addItem(get_class($model), 1, 1); - - $this->assertEquals(1, $cart->items()->first()->quantity); - - $response = $this->json('PATCH', 'api/shopr/cart/items/'.$item->id, ['quantity' => 2]) - ->assertStatus(200) - ->assertJsonFragment(['count' => 2]); - - $this->assertEquals(2, $cart->count()); - $this->assertEquals(2, $cart->items()->first()->quantity); - } - - /** @test */ - public function it_updates_the_cart_totals_correctly() - { - $cart = app(Cart::class); - $model = TestShoppable::first(); - $item = $cart->addItem(get_class($model), $model->id, 1, [], [ - ['shoppable_type' => get_class($model), 'shoppable_id' => 1], - ['shoppable_type' => get_class($model), 'shoppable_id' => 1, 'options' => ['color' => 'Green']], - ]); - - $response = $this->json('PATCH', 'api/shopr/cart/items/'.$item->id, ['quantity' => 2]) - ->assertStatus(200) - ->assertJsonFragment(['count' => 2, 'total' => 3000]); - - // Make sure the quantity and totals of all subItems are updated as well. - $subItems = $cart->items()->first()->subItems; - $this->assertEquals([2, 2], $subItems->pluck('quantity')->toArray()); - $this->assertEquals([1000, 1000], $subItems->pluck('total')->toArray()); - } - - /** @test */ - public function it_does_not_remove_discount_coupons() - { - $discount = factory(DiscountCoupon::class)->create(); - $cart = app(Cart::class); - $model = TestShoppable::first(); - $item = $cart->addItem(get_class($model), $model->id, null); - - $cart->addDiscount($discount); - - $response = $this->json('PATCH', 'api/shopr/cart/items/'.$item->id, ['quantity' => 2]) - ->assertStatus(200) - ->assertJsonFragment(['count' => 2]); - - $this->assertTrue($cart->hasDiscount($discount->code)); - $this->assertEquals(2, $cart->items()->first()->quantity); - } - - /** @test */ - public function it_updates_percentage_discount_value() - { - $discount = factory(DiscountCoupon::class)->create(['value' => 50, 'is_fixed' => 0]); - - $cart = app(Cart::class); - $model = TestShoppable::first(); - $item = $cart->addItem(get_class($model), $model->id, null); - - $cart->addDiscount($discount); - - $this->assertEquals(250, $cart->summary()['total']); - - $response = $this->json('PATCH', 'api/shopr/cart/items/'.$item->id, ['quantity' => 2]) - ->assertStatus(200) - ->assertJsonFragment(['count' => 2]); - - $this->assertEquals(500, $cart->summary()['total']); - $this->assertEquals(-500, $cart->discounts()->first()->total); - $this->assertEquals(-500, $cart->discounts()->first()->price); - $this->assertEquals('-$500.00', $cart->discounts()->first()->price_formatted); - } - - /** @test */ - public function it_fires_event() - { - $cart = app(Cart::class); - $model = TestShoppable::first(); - $item = $cart->addItem(get_class($model), 1, 1); - - Event::fake(); - - $response = $this->json('PATCH', 'api/shopr/cart/items/'.$item->id, ['quantity' => 2]) - ->assertStatus(200) - ->assertJsonFragment(['count' => 2]); - - $item = $cart->items()->first(); - - Event::assertDispatched('shopr.cart.items.updated', function ($event, $data) use ($item) { - return - $item->id === $data->id && - serialize($item) === serialize($data); - }); - } -} diff --git a/tests/Http/Cart/AddCartItemHttpTest.php b/tests/Http/Cart/AddCartItemHttpTest.php new file mode 100644 index 0000000..57a8fa6 --- /dev/null +++ b/tests/Http/Cart/AddCartItemHttpTest.php @@ -0,0 +1,101 @@ +json('POST', 'api/shopr/cart/items') + ->assertStatus(422) + ->assertJsonValidationErrors(['shoppable_type', 'shoppable_id']); + } + + /** @test */ + public function discounts_are_not_allowed() + { + $discount = factory(DiscountCoupon::class)->create(); + + $this->json('POST', 'api/shopr/cart/items', [ + 'shoppable_type' => get_class($discount), + 'shoppable_id' => $discount->id, + 'quantity' => 1, + ]) + ->assertStatus(422) + ->assertJsonValidationErrors(['shoppable_type']) + ->assertJsonFragment(['Invalid shoppable.']); + } + + /** @test */ + public function it_throws_404_error_if_shoppable_is_not_found() + { + $this->json('POST', 'api/shopr/cart/items', [ + 'shoppable_id' => 2, + 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', + ])->assertStatus(404); + } + + /** @test */ + public function it_adds_an_item() + { + $this->withoutExceptionHandling(); + + $this->assertEquals(0, Cart::count()); + + $this->json('POST', 'api/shopr/cart/items', [ + 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', + 'shoppable_id' => 1, + 'quantity' => 2, + 'options' => ['size' => 'L'], + 'sub_items' => [ + [ + 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', + 'shoppable_id' => '1', + ], + ], + 'price' => 50, + ])->assertStatus(200); + + $this->assertEquals(2, Cart::count()); + + $item = Cart::first(); + $this->assertEquals(['size' => 'L'], $item->options); + $this->assertEquals(550, $item->price); + $this->assertEquals(1, $item->sub_items->count()); + } + + /** @test */ + public function it_uses_defaults() + { + $this->withoutExceptionHandling(); + + $this->json('POST', 'api/shopr/cart/items', [ + 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', + 'shoppable_id' => 1, + ])->assertStatus(200); + + $this->assertEquals(1, Cart::count()); + + $item = Cart::first(); + $this->assertNull($item->options); + $this->assertEquals(500, $item->price); + $this->assertEquals(0, $item->sub_items->count()); + } + + /** @test */ + public function adding_an_item_returns_cart_summary() + { + $this->withoutExceptionHandling(); + + $this->json('POST', 'api/shopr/cart/items', [ + 'shoppable_type' => 'Happypixels\Shopr\Tests\Support\Models\TestShoppable', + 'shoppable_id' => 1, + 'quantity' => 2, + ])->assertJsonStructure(array_keys(Cart::get())); + } +} diff --git a/tests/Http/Cart/ClearCartHttpTest.php b/tests/Http/Cart/ClearCartHttpTest.php new file mode 100644 index 0000000..a4a4fa6 --- /dev/null +++ b/tests/Http/Cart/ClearCartHttpTest.php @@ -0,0 +1,20 @@ +withoutExceptionHandling(); + + Cart::shouldReceive('clear')->once(); + Cart::shouldReceive('get')->once()->andReturn(['result']); + + $this->json('DELETE', 'api/shopr/cart')->assertStatus(200)->assertJson(['result']); + } +} diff --git a/tests/Http/Cart/GetCartCountHttpTest.php b/tests/Http/Cart/GetCartCountHttpTest.php new file mode 100644 index 0000000..328c7fc --- /dev/null +++ b/tests/Http/Cart/GetCartCountHttpTest.php @@ -0,0 +1,19 @@ +withoutExceptionHandling(); + + Cart::shouldReceive('count')->once()->andReturn(123); + + $this->json('GET', 'api/shopr/cart/count')->assertStatus(200)->assertJson(['count' => 123]); + } +} diff --git a/tests/Http/Cart/GetCartHttpTest.php b/tests/Http/Cart/GetCartHttpTest.php new file mode 100644 index 0000000..c96ab6e --- /dev/null +++ b/tests/Http/Cart/GetCartHttpTest.php @@ -0,0 +1,19 @@ +withoutExceptionHandling(); + + Cart::shouldReceive('get')->once()->andReturn(['result']); + + $this->json('GET', 'api/shopr/cart')->assertStatus(200)->assertJson(['result']); + } +} diff --git a/tests/Http/Cart/RemoveCartItemHttpTest.php b/tests/Http/Cart/RemoveCartItemHttpTest.php new file mode 100644 index 0000000..8e37614 --- /dev/null +++ b/tests/Http/Cart/RemoveCartItemHttpTest.php @@ -0,0 +1,24 @@ + ['size' => 'L']]); + + Cart::shouldReceive('delete')->once()->with($item->id); + Cart::shouldReceive('get')->once()->andReturn(['result']); + + $this->json('DELETE', 'api/shopr/cart/items/'.$item->id) + ->assertStatus(200) + ->assertJson(['result']); + } +} diff --git a/tests/Http/Cart/UpdateCartItemHttpTest.php b/tests/Http/Cart/UpdateCartItemHttpTest.php new file mode 100644 index 0000000..b579fd4 --- /dev/null +++ b/tests/Http/Cart/UpdateCartItemHttpTest.php @@ -0,0 +1,25 @@ +withoutExceptionHandling(); + + $item = Cart::add(TestShoppable::first()); + + Cart::shouldReceive('update')->once()->with($item->id, ['quantity' => 2]); + Cart::shouldReceive('get')->once()->andReturn(['result']); + + $this->json('PATCH', 'api/shopr/cart/items/'.$item->id, ['quantity' => 2]) + ->assertStatus(200) + ->assertJsonFragment(['result']); + } +}