From 85ae0c30c578f5e0a2166f35020fdbeda7ba6ca7 Mon Sep 17 00:00:00 2001 From: Joel Male Date: Thu, 15 Aug 2024 17:21:19 +1000 Subject: [PATCH] test updates & working on calculating condition values based on orders --- README.md | 42 +- composer.json | 3 +- composer.lock | 166 +- src/Cart.php | 96 +- src/CartCondition.php | 26 +- src/Helpers/Helpers.php | 4 + tests/Helpers/ConfigConditionsMock.php | 9 + tests/Pest.php | 40 - tests/Unit/CartConditionsTest.php | 2279 +++++++++++++++--------- tests/Unit/CartTest.php | 65 +- 10 files changed, 1758 insertions(+), 972 deletions(-) create mode 100644 tests/Helpers/ConfigConditionsMock.php diff --git a/README.md b/README.md index 8217694..b44ae8a 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,14 @@ #### Getting conditions on the cart: **Cart::getConditions()** $condition->getAttributes(); // the attributes of the condition, returns an empty [] if no attributes added ``` -### Calculating condition value: **Cart::getCondition('Tax: 10%')->getCalculatedValue($subTotal)** +### Calculating condition value + +There are 2 ways to calculate the value of a condition: + +1. Using the `getCalculatedValue` method on the condition instance +2. Using the `getCalculatedValueForCondition` method on the cart instance and passing the condition name + +#### Using the `getCalculatedValue` method on the condition instance ```php $subTotal = Cart::getSubTotal(); @@ -349,6 +356,39 @@ ### Calculating condition value: **Cart::getCondition('Tax: 10%')->getCalculated $conditionCalculatedValue = $condition->getCalculatedValue($subTotal); ``` +#### Using the `getCalculatedValueForCondition` method on the cart instance + +This method automatically calculates the value of a condition by it's name based on the order of the conditions. + +```php +Cart::add([ + 'id' => 1, + 'name' => 'Apple iPhone 15', + 'price' => 200, + 'quantity' => 1, + 'attributes' => [], +]); + +$couponDiscount = new CartCondition([ + 'name' => 'Coupon Discount', + 'type' => 'discount', + 'target' => 'subtotal', + 'value' => '-200', + 'order' => 1, +]); + +$giftCard = new CartCondition([ + 'name' => 'Gift Card', + 'type' => 'discount', + 'target' => 'subtotal', + 'value' => '-200', + 'order' => 2, +]); + +Cart::getCalculatedValueForCondition('Coupon Discount'); // returns 200 +Cart::getCalculatedValueForCondition('Gift Card'); // returns 0 as the coupon discount is applied first and brings the subtotal to 0 +``` + ### Condition on items Item conditions are useful if you have discounts to be applied specifically on an item and not on the whole cart value. diff --git a/composer.json b/composer.json index d42cc70..61ec46d 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ "require-dev": { "mockery/mockery": "^1.4.2", "laravel/pint": "dev-main", - "pestphp/pest": "2.34.4" + "pestphp/pest": "2.34.4", + "larapack/dd": "dev-master" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 0fd89f9..6ec7b76 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b25e260acd3e7cf87e600ba7103131f4", + "content-hash": "b15572aabc5ce8af520d03870b1943ea", "packages": [ { "name": "brick/math", @@ -816,12 +816,12 @@ "source": { "type": "git", "url": "https://github.com/illuminate/validation.git", - "reference": "9398c6e24bf1b0ac12a7160f85bd0cc5e8f4305d" + "reference": "60474ce720aab7264ed8d75a344e8a8af911e417" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/validation/zipball/9398c6e24bf1b0ac12a7160f85bd0cc5e8f4305d", - "reference": "9398c6e24bf1b0ac12a7160f85bd0cc5e8f4305d", + "url": "https://api.github.com/repos/illuminate/validation/zipball/60474ce720aab7264ed8d75a344e8a8af911e417", + "reference": "60474ce720aab7264ed8d75a344e8a8af911e417", "shasum": "" }, "require": { @@ -869,7 +869,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-08-05T13:45:12+00:00" + "time": "2024-08-13T17:23:08+00:00" }, { "name": "nesbot/carbon", @@ -1212,12 +1212,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "51c0ff591e6295f21e1789e55a6e57457980895e" + "reference": "0470b8dc10ca08d23aeaabbed5b961c928e4d89d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/51c0ff591e6295f21e1789e55a6e57457980895e", - "reference": "51c0ff591e6295f21e1789e55a6e57457980895e", + "url": "https://api.github.com/repos/symfony/finder/zipball/0470b8dc10ca08d23aeaabbed5b961c928e4d89d", + "reference": "0470b8dc10ca08d23aeaabbed5b961c928e4d89d", "shasum": "" }, "require": { @@ -1268,7 +1268,7 @@ "type": "tidelift" } ], - "time": "2024-08-12T10:26:02+00:00" + "time": "2024-08-13T14:29:53+00:00" }, { "name": "symfony/http-foundation", @@ -1276,12 +1276,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4187b642a75b3d56ae1bf1461091eedbef2e06d6" + "reference": "79d0023c23d92d49c43be47c726e92366b4d3a81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4187b642a75b3d56ae1bf1461091eedbef2e06d6", - "reference": "4187b642a75b3d56ae1bf1461091eedbef2e06d6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/79d0023c23d92d49c43be47c726e92366b4d3a81", + "reference": "79d0023c23d92d49c43be47c726e92366b4d3a81", "shasum": "" }, "require": { @@ -1345,7 +1345,7 @@ "type": "tidelift" } ], - "time": "2024-08-06T10:13:58+00:00" + "time": "2024-08-13T12:32:31+00:00" }, { "name": "symfony/mime", @@ -1353,12 +1353,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "ab31998ee18185ce4cdea28bb52ffbda575378ee" + "reference": "52dc06994d8ee1f97bdc6a80c758c1a6bc482f78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/ab31998ee18185ce4cdea28bb52ffbda575378ee", - "reference": "ab31998ee18185ce4cdea28bb52ffbda575378ee", + "url": "https://api.github.com/repos/symfony/mime/zipball/52dc06994d8ee1f97bdc6a80c758c1a6bc482f78", + "reference": "52dc06994d8ee1f97bdc6a80c758c1a6bc482f78", "shasum": "" }, "require": { @@ -1429,7 +1429,7 @@ "type": "tidelift" } ], - "time": "2024-08-12T12:27:03+00:00" + "time": "2024-08-13T14:29:53+00:00" }, { "name": "symfony/polyfill-intl-idn", @@ -2465,6 +2465,47 @@ }, "time": "2024-08-01T12:14:16+00:00" }, + { + "name": "larapack/dd", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/larapack/dd.git", + "reference": "561b5111a13d0094b59b5c81b1572489485fb948" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/larapack/dd/zipball/561b5111a13d0094b59b5c81b1572489485fb948", + "reference": "561b5111a13d0094b59b5c81b1572489485fb948", + "shasum": "" + }, + "require": { + "symfony/var-dumper": "*" + }, + "default-branch": true, + "type": "package", + "autoload": { + "files": [ + "src/helper.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Topper", + "email": "hi@webman.io" + } + ], + "description": "`dd` is a helper method in Laravel. This package will add the `dd` to your application.", + "support": { + "issues": "https://github.com/larapack/dd/issues", + "source": "https://github.com/larapack/dd/tree/v1.1" + }, + "time": "2016-12-15T09:34:34+00:00" + }, { "name": "laravel/pint", "version": "dev-main", @@ -4918,12 +4959,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2204ca36f39ffe9bc0856123d45fd6cea52a2681" + "reference": "3c43d7ec3eb45e0eac993e86bee2905bfee5246b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2204ca36f39ffe9bc0856123d45fd6cea52a2681", - "reference": "2204ca36f39ffe9bc0856123d45fd6cea52a2681", + "url": "https://api.github.com/repos/symfony/console/zipball/3c43d7ec3eb45e0eac993e86bee2905bfee5246b", + "reference": "3c43d7ec3eb45e0eac993e86bee2905bfee5246b", "shasum": "" }, "require": { @@ -5003,7 +5044,7 @@ "type": "tidelift" } ], - "time": "2024-08-12T10:26:02+00:00" + "time": "2024-08-13T14:29:53+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5464,6 +5505,89 @@ ], "time": "2024-08-12T10:26:02+00:00" }, + { + "name": "symfony/var-dumper", + "version": "7.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "122ba59267fc82caa8f1f509332d4b94a223013b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/122ba59267fc82caa8f1f509332d4b94a223013b", + "reference": "122ba59267fc82caa8f1f509332d4b94a223013b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/7.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-08-13T14:29:53+00:00" + }, { "name": "ta-tikoma/phpunit-architecture-test", "version": "0.8.4", diff --git a/src/Cart.php b/src/Cart.php index f192a6a..2730e91 100644 --- a/src/Cart.php +++ b/src/Cart.php @@ -360,7 +360,7 @@ public function clear() * * @throws InvalidConditionException */ - public function condition($condition) + public function condition(CartCondition $condition) { if (is_array($condition)) { foreach ($condition as $c) { @@ -568,81 +568,89 @@ public function getSubTotalWithoutConditions($formatted = true) * get cart sub total * * @param bool $formatted - * @return float */ - public function getSubTotal($formatted = true) + public function getSubTotal($formatted = true): int|float { - $cart = $this->getContent(); - - $sum = $cart->sum(function (ItemCollection $item) { + $subTotal = 0.00; + // add all the items together with conditions applied + $subTotalSum = $this->getContent()->sum(function (ItemCollection $item) { return $item->getPriceSumWithConditions(false); }); // get the conditions that are meant to be applied // on the subtotal and apply it here before returning the subtotal - $conditions = $this - ->getConditions() - ->filter(function (CartCondition $cond) { - return $cond->getTarget() === 'subtotal'; + $conditionsForSubtotal = $this->getConditions() + ->filter(function (CartCondition $condition) { + return $condition->getTarget() === 'subtotal'; + }) + ->sortBy(function (CartCondition $condition) { + return $condition->getOrder(); }); - // if there is no conditions, lets just return the sum - if (! $conditions->count()) { - return Helpers::formatValue($sum, $formatted, $this->config); + if ($conditionsForSubtotal->isEmpty()) { + return Helpers::formatValue($subTotalSum, $formatted, $this->config); } - // there are conditions, lets apply it - $newTotal = 0.00; - $process = 0; + $index = 0; - $conditions->each(function (CartCondition $cond) use ($sum, &$newTotal, &$process) { + $conditionsForSubtotal->each(function (CartCondition $condition) use ($subTotalSum, &$subTotal, &$index) { + $toBeCalculated = $index > 0 ? $subTotal : $subTotalSum; - // if this is the first iteration, the toBeCalculated - // should be the sum as initial point of value. - $toBeCalculated = ($process > 0) ? $newTotal : $sum; + $subTotal = $condition->applyCondition($toBeCalculated); - $newTotal = $cond->applyCondition($toBeCalculated); - - $process++; + $index++; }); - return Helpers::formatValue((float) $newTotal, $formatted, $this->config); + return Helpers::formatValue($subTotal, $formatted, $this->config); } /** - * the new total in which conditions are already applied - * - * @return float + * Get the total of the cart with conditions applied */ - public function getTotal() + public function getTotal(): int|float { + $total = 0.00; $subTotal = $this->getSubTotal(false); - $newTotal = 0.00; - - $process = 0; - - $conditions = $this - ->getConditions() - ->filter(function (CartCondition $cond) { - return $cond->getTarget() === 'total'; + $conditionsForTotal = $this->getConditions() + ->filter(function (CartCondition $condition) { + return $condition->getTarget() === 'total'; }); // if no conditions were added, just return the sub total - if (! $conditions->count()) { + if (! $conditionsForTotal->count()) { return Helpers::formatValue($subTotal, $this->config['format_numbers'], $this->config); } - $conditions - ->each(function (CartCondition $cond) use ($subTotal, &$newTotal, &$process) { - $toBeCalculated = ($process > 0) ? $newTotal : $subTotal; + $conditionsForTotal->each(function (CartCondition $condition) use ($subTotal, &$total) { + $toBeCalculated = $total > 0 ? $total : $subTotal; - $newTotal = $cond->applyCondition($toBeCalculated); + $total = $condition->applyCondition($toBeCalculated); + }); - $process++; - }); + return Helpers::formatValue($total, $this->config['format_numbers'], $this->config); + } - return Helpers::formatValue($newTotal, $this->config['format_numbers'], $this->config); + /** + * Get the calculated value for a condition by it's name + */ + public function getCalculatedValueForCondition(string $conditionName): int|float + { + $conditions = $this->getConditions(); + + $subTotal = $this->getSubTotalWithoutConditions(false); + + foreach ($conditions as $condition) { + $conditionValue = $condition->getCalculatedValue($subTotal); + + if ($condition->getName() === $conditionName) { + return $conditionValue; + } + + $subTotal -= $conditionValue; + } + + return 0; } /** diff --git a/src/CartCondition.php b/src/CartCondition.php index e2c7a8a..dceb8f4 100644 --- a/src/CartCondition.php +++ b/src/CartCondition.php @@ -16,7 +16,7 @@ class CartCondition /** * the parsed raw value of the condition */ - public $parsedRawValue; + public $parsedRawValue = 0; /** * @param array $args (name, type, target, value) @@ -77,14 +77,20 @@ public function getAttributes() /** * the value of this the condition - * - * @return mixed */ - public function getValue() + public function getValue(): int|float { return $this->args['value']; } + /** + * Set the value of this condition + */ + public function setValue($value): void + { + $this->args['value'] = $value; + } + /** * Set the order to apply this condition. If no argument order is applied we return 0 as * indicator that no assignment has been made @@ -123,9 +129,11 @@ public function applyCondition($totalOrSubTotalOrPrice) * * @return mixed */ - public function getCalculatedValue($totalOrSubTotalOrPrice) + public function getCalculatedValue($totalOrSubTotalOrPrice = null): int|float { - $this->apply($totalOrSubTotalOrPrice, $this->getValue()); + if ($totalOrSubTotalOrPrice) { + $this->apply($totalOrSubTotalOrPrice, $this->getValue()); + } return $this->parsedRawValue; } @@ -137,6 +145,12 @@ public function getCalculatedValue($totalOrSubTotalOrPrice) */ protected function apply($totalOrSubTotalOrPrice, $conditionValue) { + if ($totalOrSubTotalOrPrice <= 0) { + $this->parsedRawValue = 0; + + return; + } + // if value has a percentage sign on it, we will get first // its percentage then we will evaluate again if the value // has a minus or plus sign so we can decide what to do with the diff --git a/src/Helpers/Helpers.php b/src/Helpers/Helpers.php index 98ea00d..11c0099 100644 --- a/src/Helpers/Helpers.php +++ b/src/Helpers/Helpers.php @@ -56,6 +56,10 @@ public static function issetAndHasValueOrAssignDefault(&$var, $default = false) public static function formatValue($value, $formatNumber, $config) { + if (! $value) { + return 0; + } + if ($formatNumber || $config['format_numbers']) { return floatval(number_format($value, $config['decimals'], $config['dec_point'], $config['thousands_sep'])); } else { diff --git a/tests/Helpers/ConfigConditionsMock.php b/tests/Helpers/ConfigConditionsMock.php new file mode 100644 index 0000000..bf208ed --- /dev/null +++ b/tests/Helpers/ConfigConditionsMock.php @@ -0,0 +1,9 @@ + true, + 'decimals' => 2, + 'dec_point' => '.', + 'thousands_sep' => ',', + 'use_cents' => false, +]; diff --git a/tests/Pest.php b/tests/Pest.php index a638bfa..a0ebdc0 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -27,43 +27,3 @@ expect()->extend('toBeOne', function () { return $this->toBe(1); }); - -/* -|-------------------------------------------------------------------------- -| Functions -|-------------------------------------------------------------------------- -| -| While Pest is very powerful out-of-the-box, you may have some testing code specific to your -| project that you don't want to repeat in every file. Here you can also expose helpers as -| global functions to help you to reduce the number of lines of code in your test files. -| -*/ - -function fillCart() -{ - $items = [ - [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 67.99, - 'quantity' => 1, - 'attributes' => [], - ], - [ - 'id' => 568, - 'name' => 'Sample Item 2', - 'price' => 69.25, - 'quantity' => 1, - 'attributes' => [], - ], - [ - 'id' => 856, - 'name' => 'Sample Item 3', - 'price' => 50.25, - 'quantity' => 1, - 'attributes' => [], - ], - ]; - - $this->cart->add($items); -} diff --git a/tests/Unit/CartConditionsTest.php b/tests/Unit/CartConditionsTest.php index c230b92..002f222 100644 --- a/tests/Unit/CartConditionsTest.php +++ b/tests/Unit/CartConditionsTest.php @@ -13,850 +13,1465 @@ $events, 'cart', 'SAMPLESESSIONKEY', - require (__DIR__ . '/../Helpers/ConfigMock.php') + require (__DIR__ . '/../Helpers/ConfigConditionsMock.php') ); - - $items = [ - [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 67.99, - 'quantity' => 1, - 'attributes' => [], - ], - [ - 'id' => 568, - 'name' => 'Sample Item 2', - 'price' => 69.25, - 'quantity' => 1, - 'attributes' => [], - ], - [ - 'id' => 856, - 'name' => 'Sample Item 3', - 'price' => 50.25, - 'quantity' => 1, - 'attributes' => [], - ], - ]; - - $this->cart->add($items); }); afterEach(function () { Mockery::close(); }); -test('subtotal', function () { - // add condition to subtotal - $condition = new CartCondition([ - 'name' => 'VAT 12.5%', - 'type' => 'tax', - 'target' => 'subtotal', - 'value' => '-5', - ]); +describe('cart level conditions', function () { + test('can discount the subtotal by dollar amount', function () { + $this->cart->add([ + 'id' => 456, + 'name' => 'Leather Shoes', + 'price' => 129.99, + 'quantity' => 1, + ]); - $this->cart->condition($condition); + // add condition to subtotal + $condition = new CartCondition([ + 'name' => '$5 Discount', + 'type' => 'tax', + 'target' => 'subtotal', + 'value' => '-5', + ]); - expect($this->cart->getSubTotal())->toEqual(182.49); + $this->cart->condition($condition); - // the total is also should be the same with sub total since our getTotal - // also depends on what is the value of subtotal - expect($this->cart->getTotal())->toEqual(182.49); + expect($this->cart->getSubTotal())->toEqual(124.99); + expect($this->cart->getTotal())->toEqual(124.99); + }); + + test('can discount the subtotal by percentage', function () { + $this->cart->add([ + 'id' => 456, + 'name' => 'Leather Shoes', + 'price' => 129.99, + 'quantity' => 1, + ]); + + // add condition to subtotal + $condition = new CartCondition([ + 'name' => '5% Off', + 'type' => 'tax', + 'target' => 'subtotal', + 'value' => '-5%', + ]); + + $this->cart->condition($condition); + + expect($this->cart->getSubTotal())->toEqual(123.49); + expect($this->cart->getTotal())->toEqual(123.49); + }); + + test('can discount subtotal with multiple negative dollar conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + + // add condition + $taxFree = new CartCondition([ + 'name' => 'Tax Free', + 'type' => 'tax', + 'target' => 'subtotal', + 'value' => '-10%', + ]); + + $freeShipping = new CartCondition([ + 'name' => 'Free Shipping', + 'type' => 'shipping', + 'target' => 'subtotal', + 'value' => '-10', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($freeShipping); + + expect($this->cart->getSubTotal())->toEqual(113.52, 'Cart should have sub total of 113.52'); + expect($this->cart->getTotal())->toEqual(113.52, 'Cart should have a total of 113.52'); + }); + + test('can discount subtotal with multiple negative percent conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + + // add condition + $taxFree = new CartCondition([ + 'name' => 'Tax Free', + 'type' => 'tax', + 'target' => 'subtotal', + 'value' => '-10%', + ]); + + $freeShipping = new CartCondition([ + 'name' => 'Site Wide Discount', + 'type' => 'shipping', + 'target' => 'subtotal', + 'value' => '-1.99%', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($freeShipping); + + expect($this->cart->getSubTotal())->toEqual(121.06, 'Cart should have sub total of 121.06'); + expect($this->cart->getTotal())->toEqual(121.06, 'Cart should have a total of 121.06'); + }); + + test('can discount subtotal with multiple mixed conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + + // add condition + $taxFree = new CartCondition([ + 'name' => 'Tax Free', + 'type' => 'tax', + 'target' => 'subtotal', + 'value' => '-10%', + ]); + + $freeShipping = new CartCondition([ + 'name' => 'Free Shipping', + 'type' => 'shipping', + 'target' => 'subtotal', + 'value' => '-10', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($freeShipping); + + expect($this->cart->getSubTotal())->toEqual(113.52, 'Cart should have sub total of 113.52'); + expect($this->cart->getTotal())->toEqual(113.52, 'Cart should have a total of 113.52'); + }); + + test('can add to the subtotal by dollar amount', function () { + $this->cart->add([ + 'id' => 456, + 'name' => 'Leather Shoes with Bundled Item', + 'price' => 129.99, + 'quantity' => 1, + ]); + + // add condition to subtotal + $condition = new CartCondition([ + 'name' => 'Bundled Item', + 'type' => 'bundled_item', + 'target' => 'subtotal', + 'value' => '7.99', + ]); + + $this->cart->condition($condition); + + expect($this->cart->getSubTotal())->toEqual(137.98, 'Cart should have sub total of 137.98'); + expect($this->cart->getTotal())->toEqual(137.98, 'Cart should have total of 137.98'); + }); + + test('can add to the subtotal by percentage', function () { + $this->cart->add([ + 'id' => 456, + 'name' => 'Leather Shoes', + 'price' => 129.99, + 'quantity' => 1, + ]); + + $condition = new CartCondition([ + 'name' => 'Tax 10%', + 'type' => 'tax', + 'target' => 'subtotal', + 'value' => '10%', + ]); + + $this->cart->condition($condition); + + expect($this->cart->getSubTotal())->toEqual(142.99, 'Cart should have sub total of 142.99'); + expect($this->cart->getTotal())->toEqual(142.99, 'Cart should have total of 142.99'); + }); + + test('can add to subtotal with multiple positive dollar conditions', function () { + $this->cart->add([ + 'id' => 456, + 'name' => 'Leather Jacket', + 'price' => 89.99, + 'quantity' => 1, + ]); + + $shippingProtection = new CartCondition([ + 'name' => 'Shipping Protection', + 'type' => 'fee', + 'target' => 'subtotal', + 'value' => '5', + ]); + + $handling = new CartCondition([ + 'name' => 'Handling', + 'type' => 'fee', + 'target' => 'subtotal', + 'value' => '10', + ]); + + $this->cart->condition($shippingProtection); + $this->cart->condition($handling); + + expect($this->cart->getSubTotal())->toEqual(104.99, 'Cart should have sub total of 104.99'); + expect($this->cart->getTotal())->toEqual(104.99, 'Cart should have a total of 104.99'); + }); + + test('can add to subtotal with multiple positive percentage conditions', function () { + $this->cart->add([ + 'id' => 456, + 'name' => 'Leather Jacket', + 'price' => 89.99, + 'quantity' => 1, + ]); + + $handlingFee = new CartCondition([ + 'name' => 'Handling Fee', + 'type' => 'fee', + 'target' => 'subtotal', + 'value' => '5%', + ]); + + $packageProtection = new CartCondition([ + 'name' => 'Package Protection', + 'type' => 'fee', + 'target' => 'subtotal', + 'value' => '10%', + ]); + + $this->cart->condition($handlingFee); + $this->cart->condition($packageProtection); + + expect($this->cart->getSubTotal())->toEqual(103.94, 'Cart should have sub total of 103.94'); + expect($this->cart->getTotal())->toEqual(103.94, 'Cart should have a total of 103.94'); + }); + + test('can add to subtotal with mixed dollar and percentage conditions', function () { + $this->cart->add([ + 'id' => 456, + 'name' => 'Sample Item', + 'price' => 100.00, + 'quantity' => 1, + ]); + + $condition1 = new CartCondition([ + 'name' => 'Shipping Fee', + 'type' => 'fee', + 'target' => 'subtotal', + 'value' => '5', + ]); + + $condition2 = new CartCondition([ + 'name' => 'Package Protection', + 'type' => 'fee', + 'target' => 'subtotal', + 'value' => '10%', + ]); + + $this->cart->condition($condition1); + $this->cart->condition($condition2); + + expect($this->cart->getSubTotal())->toEqual(115.50, 'Cart should have sub total of 115.50'); + expect($this->cart->getTotal())->toEqual(115.50, 'Cart should have a total of 115.50'); + }); + + test('can discount the total by dollar amount', function () { + $items = [ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 856, + 'name' => 'Sample Item 3', + 'price' => 50.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]; + + $this->cart->add($items); + + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(187.49, 'Cart should have total of 187.49'); + + $condition = new CartCondition([ + 'name' => '$15 Off', + 'type' => 'discount', + 'target' => 'total', + 'value' => '-15', + ]); + + $this->cart->condition($condition); + + // no changes in subtotal as the condition's target added was for total + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(172.49, 'Cart should have total of 172.49'); + }); + + test('can discount the total by percentage', function () { + $items = [ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 856, + 'name' => 'Sample Item 3', + 'price' => 50.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]; + + $this->cart->add($items); + + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(187.49, 'Cart should have total of 187.49'); + + $condition = new CartCondition([ + 'name' => 'Discount 10%', + 'type' => 'tax', + 'target' => 'total', + 'value' => '-10%', + ]); + + $this->cart->condition($condition); + + // no changes in subtotal as the condition's target added was for total + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(168.74, 'Cart should have total of 168.74'); + }); + + test('can discount total with multiple negative dollar conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(137.24, 'Cart should have total of 137.24'); + + // add conditions + $taxFree = new CartCondition([ + 'name' => 'Tax Free', + 'type' => 'tax', + 'target' => 'total', + 'value' => '-10%', + ]); + + $freeShipping = new CartCondition([ + 'name' => 'Free Shipping', + 'type' => 'shipping', + 'target' => 'total', + 'value' => '-10', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($freeShipping); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(113.52, 'Cart should have a total of 113.52'); + }); + + test('can discount total with multiple negative percent conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(137.24, 'Cart should have total of 137.24'); + + // add conditions + $taxFree = new CartCondition([ + 'name' => 'Tax Free', + 'type' => 'tax', + 'target' => 'total', + 'value' => '-10%', + ]); + + $siteWideDiscount = new CartCondition([ + 'name' => 'Site Wide Discount', + 'type' => 'discount', + 'target' => 'total', + 'value' => '-1.99%', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($siteWideDiscount); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(121.06, 'Cart should have a total of 121.06'); + }); + + test('can discount total with multiple mixed conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(137.24, 'Cart should have total of 137.24'); + + // add conditions + $taxFree = new CartCondition([ + 'name' => 'Tax Free', + 'type' => 'tax', + 'target' => 'total', + 'value' => '-10%', + ]); + + $freeShipping = new CartCondition([ + 'name' => 'Free Shipping', + 'type' => 'shipping', + 'target' => 'total', + 'value' => '-10', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($freeShipping); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(113.52, 'Cart should have a total of 113.52'); + }); + + test('can add to the total by dollar amount', function () { + $items = [ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 856, + 'name' => 'Sample Item 3', + 'price' => 50.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]; + + $this->cart->add($items); + + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(187.49, 'Cart should have total of 187.49'); + + // add condition + $condition = new CartCondition([ + 'name' => 'Shipping', + 'type' => 'tax', + 'target' => 'total', + 'value' => '12.99', + ]); + + $this->cart->condition($condition); + + // no changes in subtotal as the condition's target added was for total + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(200.48, 'Cart should have total of 200.48'); + }); + + test('can add to the total by percentage', function () { + $items = [ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 856, + 'name' => 'Sample Item 3', + 'price' => 50.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]; + + $this->cart->add($items); + + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(187.49, 'Cart should have total of 187.49'); + + // add condition + $condition = new CartCondition([ + 'name' => 'Tax 12.5%', + 'type' => 'tax', + 'target' => 'total', + 'value' => '12.5%', + ]); + + $this->cart->condition($condition); + + // no changes in subtotal as the condition's target added was for total + expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + expect($this->cart->getTotal())->toEqual(210.93, 'Cart should have total of 210.93'); + }); + + test('can add to the total with multiple positive dollar conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(137.24, 'Cart should have total of 137.24'); + + // add conditions + $taxFree = new CartCondition([ + 'name' => 'Tax', + 'type' => 'tax', + 'target' => 'total', + 'value' => '10', + ]); + + $freeShipping = new CartCondition([ + 'name' => 'Shipping', + 'type' => 'shipping', + 'target' => 'total', + 'value' => '15', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($freeShipping); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(162.24, 'Cart should have a total of 162.24'); + }); + + test('can add to the total with multiple positive percent conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(137.24, 'Cart should have total of 137.24'); + + // add conditions + $taxFree = new CartCondition([ + 'name' => 'Tax', + 'type' => 'tax', + 'target' => 'total', + 'value' => '10%', + ]); + + $siteWideDiscount = new CartCondition([ + 'name' => 'Site Wide Surcharge', + 'type' => 'discount', + 'target' => 'total', + 'value' => '1.99%', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($siteWideDiscount); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(153.97, 'Cart should have a total of 153.97'); + }); + + test('can add to the total with multiple mixed conditions', function () { + $this->cart->add([ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(137.24, 'Cart should have total of 137.24'); + + // add conditions + $taxFree = new CartCondition([ + 'name' => 'Tax', + 'type' => 'tax', + 'target' => 'total', + 'value' => '10%', + ]); + + $freeShipping = new CartCondition([ + 'name' => 'Shipping', + 'type' => 'shipping', + 'target' => 'total', + 'value' => '15', + ]); + + $this->cart->condition($taxFree); + $this->cart->condition($freeShipping); + + expect($this->cart->getSubTotal())->toEqual(137.24, 'Cart should have sub total of 137.24'); + expect($this->cart->getTotal())->toEqual(165.96, 'Cart should have a total of 165.96'); + }); + + test('can update calculated condition values', function () { + $this->cart->add([ + 'id' => 1, + 'name' => 'Apple iPhone 15', + 'price' => 200, + 'quantity' => 1, + 'attributes' => [], + ]); + + $couponDiscount = new CartCondition([ + 'name' => 'Coupon Discount', + 'type' => 'discount', + 'target' => 'subtotal', + 'value' => '-200', + 'order' => 1, + ]); + + $giftCard = new CartCondition([ + 'name' => 'Gift Card', + 'type' => 'discount', + 'target' => 'subtotal', + 'value' => '-200', + 'order' => 2, + ]); + + $this->cart->condition($couponDiscount); + $this->cart->condition($giftCard); + + expect($this->cart->getCalculatedValueForCondition('Coupon Discount'))->toEqual(200.0, 'Coupon Discount should be 200.0'); + expect($this->cart->getCalculatedValueForCondition('Gift Card'))->toEqual(0, 'Gift Card should be 0'); + expect($couponDiscount->getCalculatedValue())->toEqual(200.0, 'Coupon Discount value should be 200.0'); + expect($giftCard->getCalculatedValue())->toEqual(0, 'Gift Card value should be 0'); + })->only(); }); -test('total without condition', function () { - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); +describe('item level conditions', function () { + test('can add an item with a condition', function () { + $saleCondition = new CartCondition([ + 'name' => '5% Discount', + 'type' => 'sale', + 'value' => '-5%', + ]); - // no changes in subtotal as the condition's target added was for total - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); + $item = [ + 'id' => 1, + 'name' => 'Chicken Curry', + 'price' => 18.95, + 'quantity' => 1, + 'attributes' => [], + 'conditions' => $saleCondition, + ]; + + $this->cart->add($item); + + expect($this->cart->get(1)->getPriceSum())->toEqual(18.95); + expect($this->cart->get(1)->getPriceSumWithConditions())->toEqual(18); + expect($this->cart->getSubTotal())->toEqual(18, 'Cart should have subtotal of 18'); + expect($this->cart->getTotal())->toEqual(18, 'Cart should have total of 18'); + }); + + test('can add item with multiple negative item conditions', function () { + $saleCondition = new CartCondition([ + 'name' => '5% Sale', + 'type' => 'sale', + 'value' => '-5%', + ]); + + $tenOff = new CartCondition([ + 'name' => '$10 Off', + 'type' => 'promo', + 'value' => '-10', + ]); + + $randomCondition = new CartCondition([ + 'name' => 'Winner', + 'type' => 'winner', + 'value' => '-10', + ]); + + $item = [ + 'id' => 1, + 'name' => 'Apple Headphones', + 'price' => 369.80, + 'quantity' => 1, + 'attributes' => [], + 'conditions' => [$saleCondition, $tenOff, $randomCondition], + ]; + + $this->cart->add($item); + + expect($this->cart->get(1)->getPriceSum())->toEqual(369.80, 'Item subtotal with 1 item should be 369.80'); + expect($this->cart->get(1)->getPriceSumWithConditions())->toEqual(331.31, 'Item subtotal with 1 item should be 331.31'); + expect($this->cart->getSubTotal())->toEqual(331.31, 'Cart subtotal with 1 item should be 331.31'); + expect($this->cart->getTotal())->toEqual(331.31, 'Cart total with 1 item should be 331.31'); + }); + + test('can add item with multiple positive item conditions', function () { + $saleCondition = new CartCondition([ + 'name' => '5% Surcharge', + 'type' => 'surcharge', + 'value' => '5%', + ]); + + $tenOff = new CartCondition([ + 'name' => 'Handling', + 'type' => 'handling', + 'value' => '8.99', + ]); + + $randomCondition = new CartCondition([ + 'name' => 'Item Protection', + 'type' => 'winner', + 'value' => '14.49', + ]); + + $item = [ + 'id' => 1, + 'name' => 'Apple Headphones', + 'price' => 369.80, + 'quantity' => 1, + 'attributes' => [], + 'conditions' => [$saleCondition, $tenOff, $randomCondition], + ]; + + $this->cart->add($item); + + expect($this->cart->get(1)->getPriceSum())->toEqual(369.80, 'Item subtotal with 1 item should be 369.80'); + expect($this->cart->get(1)->getPriceSumWithConditions())->toEqual(411.77, 'Item subtotal with 1 item should be 411.77'); + expect($this->cart->getSubTotal())->toEqual(411.77, 'Cart subtotal with 1 item should be 411.77'); + expect($this->cart->getTotal())->toEqual(411.77, 'Cart total with 1 item should be 411.77'); + }); + + test('add item condition', function () { + $items = [ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 856, + 'name' => 'Sample Item 3', + 'price' => 50.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]; + + $this->cart->add($items); + + $itemCondition2 = new CartCondition([ + 'name' => 'Item Gift Pack 25.00', + 'type' => 'promo', + 'value' => '-25', + ]); + $coupon101 = new CartCondition([ + 'name' => 'COUPON 101', + 'type' => 'coupon', + 'value' => '-5%', + ]); + + $item = [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 100, + 'quantity' => 1, + 'attributes' => [], + 'conditions' => [$itemCondition2], + ]; + + $this->cart->add($item); + + // let's prove first we have 1 condition on this item + expect($this->cart->get($item['id'])['conditions'])->toHaveCount(1, 'Item should have 1 condition'); + + // now let's insert a condition on an existing item on the cart + $this->cart->addItemCondition($item['id'], $coupon101); + + expect($this->cart->get($item['id'])['conditions'])->toHaveCount(2, 'Item should have 2 conditions'); + }); + + test('add item condition restrict negative price', function () { + $items = [ + [ + 'id' => 456, + 'name' => 'Sample Item 1', + 'price' => 67.99, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 568, + 'name' => 'Sample Item 2', + 'price' => 69.25, + 'quantity' => 1, + 'attributes' => [], + ], + [ + 'id' => 856, + 'name' => 'Sample Item 3', + 'price' => 50.25, + 'quantity' => 1, + 'attributes' => [], + ], + ]; + + $this->cart->add($items); + + $condition = new CartCondition([ + 'name' => 'Substract amount but prevent negative value', + 'type' => 'promo', + 'value' => '-25', + ]); + + $item = [ + 'id' => 789, + 'name' => 'Sample Item 1', + 'price' => 20, + 'quantity' => 1, + 'attributes' => [], + 'conditions' => [ + $condition, + ], + ]; + + $this->cart->add($item); + + // Since the product price is 20 and the condition reduces it by 25, + // check that the item's price has been prevented from dropping below zero. + expect($this->cart->get($item['id'])->getPriceSumWithConditions())->toEqual(0.00, "The item's price should be prevented from going below zero."); + }); + + // test('get cart condition by condition name', function () { + // $itemCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // ]); + // $itemCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 25.00', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$itemCondition1, $itemCondition2]); + + // // get a condition applied on cart by condition name + // $condition = $this->cart->getCondition($itemCondition1->getName()); + + // expect('SALE 5%')->toEqual($condition->getName()); + // expect('total')->toEqual($condition->getTarget()); + // expect('sale')->toEqual($condition->getType()); + // expect('-5%')->toEqual($condition->getValue()); + // }); + + // test('remove cart condition by condition name', function () { + // $itemCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // ]); + // $itemCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 25.00', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$itemCondition1, $itemCondition2]); + + // // let's prove first we have now two conditions in the cart + // expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions'); + + // // now let's remove a specific condition by condition name + // $this->cart->removeCartCondition('SALE 5%'); + + // // cart should have now only 1 condition + // expect($this->cart->getConditions()->count())->toEqual(1, 'Cart should have one condition'); + // expect($this->cart->getConditions()->first()->getName())->toEqual('Item Gift Pack 25.00'); + // }); + + // test('remove item condition by condition name', function () { + // $itemCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'value' => '-5%', + // ]); + // $itemCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 25.00', + // 'type' => 'promo', + // 'value' => '-25', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // 'conditions' => [$itemCondition1, $itemCondition2], + // ]; + + // $this->cart->add($item); + + // // let's very first the item has 2 conditions in it + // expect($this->cart->get(456)['conditions'])->toHaveCount(2, 'Item should have two conditions'); + + // // now let's remove a condition on that item using the condition name + // $this->cart->removeItemCondition(456, 'SALE 5%'); + + // // now we should have only 1 condition left on that item + // expect($this->cart->get(456)['conditions'])->toHaveCount(1, 'Item should have one condition left'); + // }); + + // test('remove item condition by condition name scenario two', function () { + // // NOTE: in this scenario, we will add the conditions not in array format + // $itemCondition = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'value' => '-5%', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // 'conditions' => $itemCondition, // <--not in array format + // ]; + + // $this->cart->add($item); + + // // let's very first the item has 2 conditions in it + // expect($this->cart->get(456)['conditions'])->not->toBeEmpty('Item should have one condition in it.'); + + // // now let's remove a condition on that item using the condition name + // $this->cart->removeItemCondition(456, 'SALE 5%'); + + // // now we should have only 1 condition left on that item + // expect($this->cart->get(456)['conditions'])->toBeEmpty('Item should have no condition now'); + // }); + + // test('clear item conditions', function () { + // $itemCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'value' => '-5%', + // ]); + // $itemCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 25.00', + // 'type' => 'promo', + // 'value' => '-25', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // 'conditions' => [$itemCondition1, $itemCondition2], + // ]; + + // $this->cart->add($item); + + // // let's very first the item has 2 conditions in it + // expect($this->cart->get(456)['conditions'])->toHaveCount(2, 'Item should have two conditions'); + + // // now let's remove all condition on that item + // $this->cart->clearItemConditions(456); + + // // now we should have only 0 condition left on that item + // expect($this->cart->get(456)['conditions'])->toHaveCount(0, 'Item should have no conditions now'); + // }); + + // test('clear cart conditions', function () { + // // NOTE: + // // This only clears all conditions that has been added in a cart bases + // // this does not remove conditions on per item bases + // $itemCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // ]); + // $itemCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 25.00', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$itemCondition1, $itemCondition2]); + + // // let's prove first we have now two conditions in the cart + // expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions'); + + // // now let's clear cart conditions + // $this->cart->clearCartConditions(); + + // // cart should have now only 1 condition + // expect($this->cart->getConditions()->count())->toEqual(0, 'Cart should have no conditions now'); + // }); + + // test('get calculated value of a condition', function () { + // $this->cart->clear(); + + // $cartCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // ]); + // $cartCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 25.00', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$cartCondition1, $cartCondition2]); + + // $subTotal = $this->cart->getSubTotal(); + + // expect($subTotal)->toEqual(100, 'Subtotal should be 100'); + + // // way 1 + // // now we will get the calculated value of the condition 1 + // $cond1 = $this->cart->getCondition('SALE 5%'); + // expect($cond1->getCalculatedValue($subTotal))->toEqual(5, 'The calculated value must be 5'); + + // // way 2 + // // get all cart conditions and get their calculated values + // $conditions = $this->cart->getConditions(); + // expect($conditions['SALE 5%']->getCalculatedValue($subTotal))->toEqual(5, 'First condition calculated value must be 5'); + // expect($conditions['Item Gift Pack 25.00']->getCalculatedValue($subTotal))->toEqual(25, 'First condition calculated value must be 5'); + // }); + + // test('get conditions by type', function () { + // $cartCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // ]); + // $cartCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 25.00', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // ]); + // $cartCondition3 = new CartCondition([ + // 'name' => 'Item Less 8%', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-8%', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$cartCondition1, $cartCondition2, $cartCondition3]); + + // // now lets get all conditions added in the cart with the type "promo" + // $promoConditions = $this->cart->getConditionsByType('promo'); + + // expect($promoConditions->count())->toEqual(2, 'We should have 2 items as promo condition type.'); + // }); + + // test('remove conditions by type', function () { + // // NOTE: + // // when add a new condition, the condition's name will be the key to be use + // // to access the condition. For some reasons, if the condition name contains + // // a "dot" on it ("."), for example adding a condition with name "SALE 35.00" + // // this will cause issues when removing this condition by name, this will not be removed + // // so when adding a condition, the condition name should not contain any "period" (.) + // // to avoid any issues removing it using remove method: removeCartCondition($conditionName); + // $cartCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // ]); + // $cartCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 20', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // ]); + // $cartCondition3 = new CartCondition([ + // 'name' => 'Item Less 8%', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-8%', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$cartCondition1, $cartCondition2, $cartCondition3]); + + // // now lets remove all conditions added in the cart with the type "promo" + // $this->cart->removeConditionsByType('promo'); + + // expect($this->cart->getConditions()->count())->toEqual(1, 'We should have 1 condition remaining as promo conditions type has been removed.'); + // }); + + // test('add cart condition without condition attributes', function () { + // $cartCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$cartCondition1]); + + // // prove first we have now the condition on the cart + // $contition = $this->cart->getCondition('SALE 5%'); + // expect($contition->getName())->toEqual('SALE 5%'); + + // // when get attribute is called and there is no attributes added, + // // it should return an empty array + // $conditionAttribute = $contition->getAttributes(); + // expect($conditionAttribute)->toBeArray(); + // }); + + // test('add cart condition with condition attributes', function () { + // $cartCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // 'attributes' => [ + // 'description' => 'october fest promo sale', + // 'sale_start_date' => '2015-01-20', + // 'sale_end_date' => '2015-01-30', + // ], + // ]); + + // $item = [ + // 'id' => 456, + // 'name' => 'Sample Item 1', + // 'price' => 100, + // 'quantity' => 1, + // 'attributes' => [], + // ]; + + // $this->cart->add($item); + + // $this->cart->condition([$cartCondition1]); + + // // prove first we have now the condition on the cart + // $contition = $this->cart->getCondition('SALE 5%'); + // expect($contition->getName())->toEqual('SALE 5%'); + + // // when get attribute is called and there is no attributes added, + // // it should return an empty array + // $conditionAttributes = $contition->getAttributes(); + // expect($conditionAttributes)->toBeArray(); + // expect($conditionAttributes)->toHaveKey('description'); + // expect($conditionAttributes)->toHaveKey('sale_start_date'); + // expect($conditionAttributes)->toHaveKey('sale_end_date'); + // expect($conditionAttributes['description'])->toEqual('october fest promo sale'); + // expect($conditionAttributes['sale_start_date'])->toEqual('2015-01-20'); + // expect($conditionAttributes['sale_end_date'])->toEqual('2015-01-30'); + // }); + + // test('get order from condition', function () { + // $cartCondition1 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // 'order' => 2, + // ]); + // $cartCondition2 = new CartCondition([ + // 'name' => 'Item Gift Pack 20', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // 'order' => '3', + // ]); + // $cartCondition3 = new CartCondition([ + // 'name' => 'Item Less 8%', + // 'type' => 'tax', + // 'target' => 'total', + // 'value' => '-8%', + // 'order' => 'first', + // ]); + + // expect($cartCondition1->getOrder())->toEqual(2); + // expect($cartCondition2->getOrder())->toEqual(3); + // // numeric string is converted to integer + // expect($cartCondition3->getOrder())->toEqual(0); + + // // no numeric string is converted to 0 + // $this->cart->condition($cartCondition1); + // $this->cart->condition($cartCondition2); + // $this->cart->condition($cartCondition3); + + // $conditions = $this->cart->getConditions(); + + // expect($conditions->shift()->getType())->toEqual('sale'); + // expect($conditions->shift()->getType())->toEqual('promo'); + // expect($conditions->shift()->getType())->toEqual('tax'); + // }); + + // test('condition ordering', function () { + // $cartCondition1 = new CartCondition([ + // 'name' => 'TAX', + // 'type' => 'tax', + // 'target' => 'total', + // 'value' => '-8%', + // 'order' => 5, + // ]); + // $cartCondition2 = new CartCondition([ + // 'name' => 'SALE 5%', + // 'type' => 'sale', + // 'target' => 'total', + // 'value' => '-5%', + // 'order' => 2, + // ]); + // $cartCondition3 = new CartCondition([ + // 'name' => 'Item Gift Pack 20', + // 'type' => 'promo', + // 'target' => 'total', + // 'value' => '-25', + // 'order' => 1, + // ]); + + // $this->cart->condition($cartCondition1); + // $this->cart->condition($cartCondition2); + // $this->cart->condition($cartCondition3); + + // expect($this->cart->getConditions()->first()->getName())->toEqual('Item Gift Pack 20'); + // expect($this->cart->getConditions()->last()->getName())->toEqual('TAX'); + // }); - // total should be the same as subtotal - expect($this->cart->getTotal())->toEqual(187.49, 'Cart should have a total of 187.49'); -}); - -test('total with condition', function () { - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // add condition - $condition = new CartCondition([ - 'name' => 'VAT 12.5%', - 'type' => 'tax', - 'target' => 'total', - 'value' => '12.5%', - ]); - - $this->cart->condition($condition); - - // no changes in subtotal as the condition's target added was for total - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // total should be changed - $this->cart->setDecimals(5); - - expect($this->cart->getTotal())->toEqual(210.92625, 'Cart should have a total of 210.92625'); -}); - -test('total with multiple conditions added scenario one', function () { - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // add condition - $condition1 = new CartCondition([ - 'name' => 'VAT 12.5%', - 'type' => 'tax', - 'target' => 'total', - 'value' => '12.5%', - ]); - - $condition2 = new CartCondition([ - 'name' => 'Express Shipping $15', - 'type' => 'shipping', - 'target' => 'total', - 'value' => '+15', - ]); - - $this->cart->condition($condition1); - $this->cart->condition($condition2); - - // no changes in subtotal as the condition's target added was for subtotal - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // total should be changed - $this->cart->setDecimals(5); - expect($this->cart->getTotal())->toEqual(225.92625, 'Cart should have a total of 225.92625'); -}); - -test('total with multiple conditions added scenario two', function () { - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // add condition - $condition1 = new CartCondition([ - 'name' => 'VAT 12.5%', - 'type' => 'tax', - 'target' => 'total', - 'value' => '12.5%', - ]); - $condition2 = new CartCondition([ - 'name' => 'Express Shipping $15', - 'type' => 'shipping', - 'target' => 'total', - 'value' => '-15', - ]); - - $this->cart->condition($condition1); - $this->cart->condition($condition2); - - // no changes in subtotal as the condition's target added was for subtotal - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // total should be changed - $this->cart->setDecimals(5); - expect($this->cart->getTotal())->toEqual(195.92625, 'Cart should have a total of 195.92625'); -}); - -test('total with multiple conditions added scenario three', function () { - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // add condition - $condition1 = new CartCondition([ - 'name' => 'VAT 12.5%', - 'type' => 'tax', - 'target' => 'total', - 'value' => '-12.5%', - ]); - $condition2 = new CartCondition([ - 'name' => 'Express Shipping $15', - 'type' => 'shipping', - 'target' => 'total', - 'value' => '-15', - ]); - - $this->cart->condition($condition1); - $this->cart->condition($condition2); - - // no changes in subtotal as the condition's target added was for total - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // total should be changed - $this->cart->setDecimals(5); - expect($this->cart->getTotal())->toEqual(149.05375, 'Cart should have a total of 149.05375'); -}); - -test('cart multiple conditions can be added once by array', function () { - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // add condition - $condition1 = new CartCondition([ - 'name' => 'VAT 12.5%', - 'type' => 'tax', - 'target' => 'total', - 'value' => '-12.5%', - ]); - $condition2 = new CartCondition([ - 'name' => 'Express Shipping $15', - 'type' => 'shipping', - 'target' => 'total', - 'value' => '-15', - ]); - - $this->cart->condition([$condition1, $condition2]); - - // no changes in subtotal as the condition's target added was for total - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // total should be changed - $this->cart->setDecimals(5); - expect($this->cart->getTotal())->toEqual(149.05375, 'Cart should have a total of 149.05375'); -}); - -test('total with multiple conditions added scenario four', function () { - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // add condition - $condition1 = new CartCondition([ - 'name' => 'COUPON LESS 12.5%', - 'type' => 'tax', - 'target' => 'total', - 'value' => '-12.5%', - ]); - $condition2 = new CartCondition([ - 'name' => 'Express Shipping $15', - 'type' => 'shipping', - 'target' => 'total', - 'value' => '+15', - ]); - - $this->cart->condition($condition1); - $this->cart->condition($condition2); - - // no changes in subtotal as the condition's target added was for total - expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49'); - - // total should be changed - $this->cart->setDecimals(5); - expect($this->cart->getTotal())->toEqual(179.05375, 'Cart should have a total of 179.05375'); -}); - -test('add item with condition', function () { - $this->cart->clear(); - - $condition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'tax', - 'value' => '-5%', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => $condition1, - ]; - - $this->cart->add($item); - - expect($this->cart->get(456)->getPriceSumWithConditions())->toEqual(95); - expect($this->cart->getSubTotal())->toEqual(95); -}); - -test('add item with multiple item conditions in multiple condition instance', function () { - $this->cart->clear(); - - $itemCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'value' => '-5%', - ]); - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'value' => '-25', - ]); - $itemCondition3 = new CartCondition([ - 'name' => 'MISC', - 'type' => 'misc', - 'value' => '+10', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => [$itemCondition1, $itemCondition2, $itemCondition3], - ]; - - $this->cart->add($item); - - expect($this->cart->get(456)->getPriceSumWithConditions())->toEqual(80.00, 'Item subtotal with 1 item should be 80'); - expect($this->cart->getSubTotal())->toEqual(80.00, 'Cart subtotal with 1 item should be 80'); -}); - -test('add item with multiple item conditions with target omitted', function () { - $this->cart->clear(); - - // NOTE: - // $condition1 and $condition4 should not be included in calculation - // as the target is not for item, remember that when adding - // conditions in per-item bases, the condition's target should - // have a value of item - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'value' => '-25', - ]); - $itemCondition3 = new CartCondition([ - 'name' => 'MISC', - 'type' => 'misc', - 'value' => '+10', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => [$itemCondition2, $itemCondition3], - ]; - - $this->cart->add($item); - - expect($this->cart->get(456)->getPriceSumWithConditions())->toEqual(85.00, 'Cart subtotal with 1 item should be 85'); - expect($this->cart->getSubTotal())->toEqual(85.00, 'Cart subtotal with 1 item should be 85'); -}); - -test('add item condition', function () { - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'value' => '-25', - ]); - $coupon101 = new CartCondition([ - 'name' => 'COUPON 101', - 'type' => 'coupon', - 'value' => '-5%', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => [$itemCondition2], - ]; - - $this->cart->add($item); - - // let's prove first we have 1 condition on this item - expect($this->cart->get($item['id'])['conditions'])->toHaveCount(1, 'Item should have 1 condition'); - - // now let's insert a condition on an existing item on the cart - $this->cart->addItemCondition($item['id'], $coupon101); - - expect($this->cart->get($item['id'])['conditions'])->toHaveCount(2, 'Item should have 2 conditions'); -}); - -test('add item condition restrict negative price', function () { - $condition = new CartCondition([ - 'name' => 'Substract amount but prevent negative value', - 'type' => 'promo', - 'value' => '-25', - ]); - - $item = [ - 'id' => 789, - 'name' => 'Sample Item 1', - 'price' => 20, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => [ - $condition, - ], - ]; - - $this->cart->add($item); - - // Since the product price is 20 and the condition reduces it by 25, - // check that the item's price has been prevented from dropping below zero. - expect($this->cart->get($item['id'])->getPriceSumWithConditions())->toEqual(0.00, "The item's price should be prevented from going below zero."); -}); - -test('get cart condition by condition name', function () { - $itemCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - ]); - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$itemCondition1, $itemCondition2]); - - // get a condition applied on cart by condition name - $condition = $this->cart->getCondition($itemCondition1->getName()); - - expect('SALE 5%')->toEqual($condition->getName()); - expect('total')->toEqual($condition->getTarget()); - expect('sale')->toEqual($condition->getType()); - expect('-5%')->toEqual($condition->getValue()); -}); - -test('remove cart condition by condition name', function () { - $itemCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - ]); - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$itemCondition1, $itemCondition2]); - - // let's prove first we have now two conditions in the cart - expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions'); - - // now let's remove a specific condition by condition name - $this->cart->removeCartCondition('SALE 5%'); - - // cart should have now only 1 condition - expect($this->cart->getConditions()->count())->toEqual(1, 'Cart should have one condition'); - expect($this->cart->getConditions()->first()->getName())->toEqual('Item Gift Pack 25.00'); -}); - -test('remove item condition by condition name', function () { - $itemCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'value' => '-5%', - ]); - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'value' => '-25', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => [$itemCondition1, $itemCondition2], - ]; - - $this->cart->add($item); - - // let's very first the item has 2 conditions in it - expect($this->cart->get(456)['conditions'])->toHaveCount(2, 'Item should have two conditions'); - - // now let's remove a condition on that item using the condition name - $this->cart->removeItemCondition(456, 'SALE 5%'); - - // now we should have only 1 condition left on that item - expect($this->cart->get(456)['conditions'])->toHaveCount(1, 'Item should have one condition left'); -}); - -test('remove item condition by condition name scenario two', function () { - // NOTE: in this scenario, we will add the conditions not in array format - $itemCondition = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'value' => '-5%', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => $itemCondition, // <--not in array format - ]; - - $this->cart->add($item); - - // let's very first the item has 2 conditions in it - expect($this->cart->get(456)['conditions'])->not->toBeEmpty('Item should have one condition in it.'); - - // now let's remove a condition on that item using the condition name - $this->cart->removeItemCondition(456, 'SALE 5%'); - - // now we should have only 1 condition left on that item - expect($this->cart->get(456)['conditions'])->toBeEmpty('Item should have no condition now'); -}); - -test('clear item conditions', function () { - $itemCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'value' => '-5%', - ]); - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'value' => '-25', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - 'conditions' => [$itemCondition1, $itemCondition2], - ]; - - $this->cart->add($item); - - // let's very first the item has 2 conditions in it - expect($this->cart->get(456)['conditions'])->toHaveCount(2, 'Item should have two conditions'); - - // now let's remove all condition on that item - $this->cart->clearItemConditions(456); - - // now we should have only 0 condition left on that item - expect($this->cart->get(456)['conditions'])->toHaveCount(0, 'Item should have no conditions now'); -}); - -test('clear cart conditions', function () { - // NOTE: - // This only clears all conditions that has been added in a cart bases - // this does not remove conditions on per item bases - $itemCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - ]); - $itemCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$itemCondition1, $itemCondition2]); - - // let's prove first we have now two conditions in the cart - expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions'); - - // now let's clear cart conditions - $this->cart->clearCartConditions(); - - // cart should have now only 1 condition - expect($this->cart->getConditions()->count())->toEqual(0, 'Cart should have no conditions now'); -}); - -test('get calculated value of a condition', function () { - $this->cart->clear(); - - $cartCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - ]); - $cartCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$cartCondition1, $cartCondition2]); - - $subTotal = $this->cart->getSubTotal(); - - expect($subTotal)->toEqual(100, 'Subtotal should be 100'); - - // way 1 - // now we will get the calculated value of the condition 1 - $cond1 = $this->cart->getCondition('SALE 5%'); - expect($cond1->getCalculatedValue($subTotal))->toEqual(5, 'The calculated value must be 5'); - - // way 2 - // get all cart conditions and get their calculated values - $conditions = $this->cart->getConditions(); - expect($conditions['SALE 5%']->getCalculatedValue($subTotal))->toEqual(5, 'First condition calculated value must be 5'); - expect($conditions['Item Gift Pack 25.00']->getCalculatedValue($subTotal))->toEqual(25, 'First condition calculated value must be 5'); -}); - -test('get conditions by type', function () { - $cartCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - ]); - $cartCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 25.00', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - ]); - $cartCondition3 = new CartCondition([ - 'name' => 'Item Less 8%', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-8%', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$cartCondition1, $cartCondition2, $cartCondition3]); - - // now lets get all conditions added in the cart with the type "promo" - $promoConditions = $this->cart->getConditionsByType('promo'); - - expect($promoConditions->count())->toEqual(2, 'We should have 2 items as promo condition type.'); -}); - -test('remove conditions by type', function () { - // NOTE: - // when add a new condition, the condition's name will be the key to be use - // to access the condition. For some reasons, if the condition name contains - // a "dot" on it ("."), for example adding a condition with name "SALE 35.00" - // this will cause issues when removing this condition by name, this will not be removed - // so when adding a condition, the condition name should not contain any "period" (.) - // to avoid any issues removing it using remove method: removeCartCondition($conditionName); - $cartCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - ]); - $cartCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 20', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - ]); - $cartCondition3 = new CartCondition([ - 'name' => 'Item Less 8%', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-8%', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$cartCondition1, $cartCondition2, $cartCondition3]); - - // now lets remove all conditions added in the cart with the type "promo" - $this->cart->removeConditionsByType('promo'); - - expect($this->cart->getConditions()->count())->toEqual(1, 'We should have 1 condition remaining as promo conditions type has been removed.'); -}); - -test('add cart condition without condition attributes', function () { - $cartCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$cartCondition1]); - - // prove first we have now the condition on the cart - $contition = $this->cart->getCondition('SALE 5%'); - expect($contition->getName())->toEqual('SALE 5%'); - - // when get attribute is called and there is no attributes added, - // it should return an empty array - $conditionAttribute = $contition->getAttributes(); - expect($conditionAttribute)->toBeArray(); -}); - -test('add cart condition with condition attributes', function () { - $cartCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - 'attributes' => [ - 'description' => 'october fest promo sale', - 'sale_start_date' => '2015-01-20', - 'sale_end_date' => '2015-01-30', - ], - ]); - - $item = [ - 'id' => 456, - 'name' => 'Sample Item 1', - 'price' => 100, - 'quantity' => 1, - 'attributes' => [], - ]; - - $this->cart->add($item); - - $this->cart->condition([$cartCondition1]); - - // prove first we have now the condition on the cart - $contition = $this->cart->getCondition('SALE 5%'); - expect($contition->getName())->toEqual('SALE 5%'); - - // when get attribute is called and there is no attributes added, - // it should return an empty array - $conditionAttributes = $contition->getAttributes(); - expect($conditionAttributes)->toBeArray(); - expect($conditionAttributes)->toHaveKey('description'); - expect($conditionAttributes)->toHaveKey('sale_start_date'); - expect($conditionAttributes)->toHaveKey('sale_end_date'); - expect($conditionAttributes['description'])->toEqual('october fest promo sale'); - expect($conditionAttributes['sale_start_date'])->toEqual('2015-01-20'); - expect($conditionAttributes['sale_end_date'])->toEqual('2015-01-30'); -}); - -test('get order from condition', function () { - $cartCondition1 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - 'order' => 2, - ]); - $cartCondition2 = new CartCondition([ - 'name' => 'Item Gift Pack 20', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - 'order' => '3', - ]); - $cartCondition3 = new CartCondition([ - 'name' => 'Item Less 8%', - 'type' => 'tax', - 'target' => 'total', - 'value' => '-8%', - 'order' => 'first', - ]); - - expect($cartCondition1->getOrder())->toEqual(2); - expect($cartCondition2->getOrder())->toEqual(3); - // numeric string is converted to integer - expect($cartCondition3->getOrder())->toEqual(0); - - // no numeric string is converted to 0 - $this->cart->condition($cartCondition1); - $this->cart->condition($cartCondition2); - $this->cart->condition($cartCondition3); - - $conditions = $this->cart->getConditions(); - - expect($conditions->shift()->getType())->toEqual('sale'); - expect($conditions->shift()->getType())->toEqual('promo'); - expect($conditions->shift()->getType())->toEqual('tax'); -}); - -test('condition ordering', function () { - $cartCondition1 = new CartCondition([ - 'name' => 'TAX', - 'type' => 'tax', - 'target' => 'total', - 'value' => '-8%', - 'order' => 5, - ]); - $cartCondition2 = new CartCondition([ - 'name' => 'SALE 5%', - 'type' => 'sale', - 'target' => 'total', - 'value' => '-5%', - 'order' => 2, - ]); - $cartCondition3 = new CartCondition([ - 'name' => 'Item Gift Pack 20', - 'type' => 'promo', - 'target' => 'total', - 'value' => '-25', - 'order' => 1, - ]); - - $this->cart->condition($cartCondition1); - $this->cart->condition($cartCondition2); - $this->cart->condition($cartCondition3); - - expect($this->cart->getConditions()->first()->getName())->toEqual('Item Gift Pack 20'); - expect($this->cart->getConditions()->last()->getName())->toEqual('TAX'); }); diff --git a/tests/Unit/CartTest.php b/tests/Unit/CartTest.php index 6f8daf8..a4eaad3 100644 --- a/tests/Unit/CartTest.php +++ b/tests/Unit/CartTest.php @@ -21,39 +21,50 @@ Mockery::close(); }); -test('cart can add item', function () { - $this->cart->add(455, 'Sample Item', 100.99, 2, []); +describe('cart', function () { + test('can add an item', function () { + $this->cart->add(1, 'Sample Item', 100.99, 2, []); - expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty'); - expect($this->cart->getContent()->count())->toEqual(1, 'Cart content should be 1'); - expect($this->cart->getContent()->first()['id'])->toEqual(455, 'Item added has ID of 455 so first content ID should be 455'); - expect($this->cart->getContent()->first()['price'])->toEqual(100.99, 'Item added has price of 100.99 so first content price should be 100.99'); -}); + expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty'); + expect($this->cart->getContent()->count())->toEqual(1, 'Cart content should be 1'); + expect($this->cart->getContent()->first()['id'])->toEqual(1, 'Item added has ID of 1 so first content ID should be 1'); + expect($this->cart->getContent()->first()['price'])->toEqual(100.99, 'Item added has price of 100.99 so first content price should be 100.99'); + }); -test('cart can add item with no price', function () { - $this->cart->add(455, 'Sample Item', 0, 2, []); + test('can add an item in cents', function () { + $this->cart->add(1, 'Sample Item', 10099, 2, []); - expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty'); - expect($this->cart->getContent()->count())->toEqual(1, 'Cart content should be 1'); - expect($this->cart->getContent()->first()['id'])->toEqual(455, 'Item added has ID of 455 so first content ID should be 455'); - expect($this->cart->getTotal())->toEqual(0, 'Total should be 0'); -}); + expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty'); + expect($this->cart->getContent()->count())->toEqual(1, 'Cart content should be 1'); + expect($this->cart->getContent()->first()['id'])->toEqual(1, 'Item added has ID of 1 so first content ID should be 1'); + expect($this->cart->getContent()->first()['price'])->toEqual(10099, 'Item added has price of 100.99 so first content price should be 100.99'); + })->only(); -test('cart can add items as array', function () { - $item = [ - 'id' => 456, - 'name' => 'Sample Item', - 'price' => 67.99, - 'quantity' => 4, - 'attributes' => [], - ]; + test('can add an item with no price', function () { + $this->cart->add(455, 'Sample Item', 0, 2, []); - $this->cart->add($item); + expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty'); + expect($this->cart->getContent()->count())->toEqual(1, 'Cart content should be 1'); + expect($this->cart->getContent()->first()['id'])->toEqual(455, 'Item added has ID of 455 so first content ID should be 455'); + expect($this->cart->getTotal())->toEqual(0, 'Total should be 0'); + }); - expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty'); - expect($this->cart->getContent()->count())->toEqual(1, 'Cart should have 1 item on it'); - expect($this->cart->getContent()->first()['id'])->toEqual(456, 'The first content must have ID of 456'); - expect($this->cart->getContent()->first()['name'])->toEqual('Sample Item', 'The first content must have name of "Sample Item"'); + test('can add items as array', function () { + $item = [ + 'id' => 456, + 'name' => 'Sample Item', + 'price' => 67.99, + 'quantity' => 4, + 'attributes' => [], + ]; + + $this->cart->add($item); + + expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty'); + expect($this->cart->getContent()->count())->toEqual(1, 'Cart should have 1 item on it'); + expect($this->cart->getContent()->first()['id'])->toEqual(456, 'The first content must have ID of 456'); + expect($this->cart->getContent()->first()['name'])->toEqual('Sample Item', 'The first content must have name of "Sample Item"'); + }); }); test('cart can add items with multidimensional array', function () {