Compare commits

...
20 Commits
Author SHA1 Message Date
Joel Male 61475a7060 1.0.6 2024-08-26 10:58:14 +10:00
Joel Male 6604cf984d fix tests & validation errors 2024-08-26 10:57:02 +10:00
Joel Male 7d955d722c Merge pull request #9 from wearepixel/task/add-items-with-no-price
Add items with no price
2024-08-26 10:49:47 +10:00
Joel Male 54d94d8d12 Add items with no price 2024-08-26 10:38:20 +10:00
Joel Male e49ebe02e0 1.0.4 2024-08-20 16:58:57 +10:00
Joel Male 28b451583d Merge pull request #8 from wearepixel/issue/cart-conditions-break-if-one-makes-the-subtotal-free-and-another-adds-to-the-subtotal
Cart conditions break if one makes the subtotal free and another adds to the subtotal
2024-08-20 16:58:24 +10:00
Joel Male 3673f360b7 feat: fix tests 2024-08-20 16:57:11 +10:00
Joel Male f6447ad4d9 feat: add failing tests 2024-08-20 16:57:06 +10:00
Joel Male b102dd2356 1.0.3 2024-08-19 10:35:54 +10:00
Joel Male 25fc662c05 formatting 2024-08-16 19:23:01 +10:00
Joel Male 57328c2f64 legal jargon 2024-08-16 19:23:01 +10:00
github-actions[bot] 4d19211f2c Merge pull request #5 from wearepixel/dependabot/composer/master/pestphp/pest-2.35.0
Bump pestphp/pest from 2.34.4 to 2.35.0
2024-08-16 09:20:37 +00:00
dependabot[bot] 8cee4f6208 Bump pestphp/pest from 2.34.4 to 2.35.0
Bumps [pestphp/pest](https://github.com/pestphp/pest) from 2.34.4 to 2.35.0.
- [Release notes](https://github.com/pestphp/pest/releases)
- [Changelog](https://github.com/pestphp/pest/blob/2.x/RELEASE.md)
- [Commits](https://github.com/pestphp/pest/compare/v2.34.4...v2.35.0)

---
updated-dependencies:
- dependency-name: pestphp/pest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-16 09:20:26 +00:00
Joel Male 0ec0668bcf code cleanup 2024-08-16 19:19:14 +10:00
Joel Male 9a4b5382bd 1.0.2 2024-08-16 16:55:10 +10:00
Joel Male 95054f41a4 feature: quantities now support decimals
Merge pull request #4 from wearepixel/feature/quantities-now-support-decimals
2024-08-16 16:52:13 +10:00
Joel Male 0a9bcc2832 quantities now support decimals (so adding 1.1, or setting it to 1.1) 2024-08-16 16:49:59 +10:00
Joel Male 8c8c90c170 Feature: Cart item orders never change
Merge pull request #2 from wearepixel/feature/cart-item-orders-never-change
2024-08-16 16:33:26 +10:00
Pixel Tech 18c753f39b Apply Laravel Pint
Signed-off-by: Pixel Tech <tech@wearepixel.com.au>
2024-08-16 06:33:03 +00:00
Joel Male d12a566366 cart item orders never change 2024-08-16 16:29:46 +10:00
16 changed files with 557 additions and 284 deletions
+4 -4
View File
@@ -3,15 +3,15 @@ version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "develop"
target-branch: "master"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "deps"
- package-ecosystem: "composer"
directory: "/"
target-branch: "develop"
target-branch: "master"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "deps"
+2 -1
View File
@@ -2,4 +2,5 @@
.DS_Store
.idea
.phpunit.result.cache
READMETODO.md
READMETODO.md
build
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) Pixel <hello@wearepixel.com.au>
Copyright (c) Pixel Digital Pty Ltd <hello@wearepixel.com.au>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+8
View File
@@ -499,10 +499,18 @@ #### Add condition to existing Item on the cart: **Cart::addItemCondition($produ
### Clearing Cart Conditions: **Cart::clearCartConditions()**
This clears all cart level conditions, and does not affect item level conditions.
```php
Cart::clearCartConditions()
```
If you wish to clear all conditions from all items and the cart, use **Cart::clearAllConditions()**
```php
Cart::clearAllConditions()
```
### Remove a specific cart condtion: **Cart::removeCartCondition($conditionName)**
```php
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "wearepixel/laravel-cart",
"version": "1.0.1",
"version": "1.0.6",
"description": "A cart implementation for Laravel",
"keywords": [
"laravel",
@@ -19,7 +19,7 @@
}
],
"require": {
"php": ">=8.0",
"php": ">=8.1",
"illuminate/support": "^9.0|^10.0|^11.0",
"illuminate/validation": "^9.0|^10.0|^11.0",
"illuminate/translation": "^9.0|^10.0|^11.0"
@@ -27,7 +27,7 @@
"require-dev": {
"mockery/mockery": "^1.4.2",
"laravel/pint": "dev-main",
"pestphp/pest": "2.34.4",
"pestphp/pest": "2.35.0",
"larapack/dd": "dev-master"
},
"autoload": {
Generated
+28 -27
View File
@@ -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": "b15572aabc5ce8af520d03870b1943ea",
"content-hash": "f22ad76e04818683f42bf13aa7a5e4ef",
"packages": [
{
"name": "brick/math",
@@ -2963,36 +2963,36 @@
},
{
"name": "pestphp/pest",
"version": "v2.34.4",
"version": "v2.35.0",
"source": {
"type": "git",
"url": "https://github.com/pestphp/pest.git",
"reference": "6a1161ead830294ef8e21fab83c0bd118b0df7cc"
"reference": "d0ff2c8ec294b7aa7fcb0f3ddc4fdec864234646"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pestphp/pest/zipball/6a1161ead830294ef8e21fab83c0bd118b0df7cc",
"reference": "6a1161ead830294ef8e21fab83c0bd118b0df7cc",
"url": "https://api.github.com/repos/pestphp/pest/zipball/d0ff2c8ec294b7aa7fcb0f3ddc4fdec864234646",
"reference": "d0ff2c8ec294b7aa7fcb0f3ddc4fdec864234646",
"shasum": ""
},
"require": {
"brianium/paratest": "^7.3.1",
"nunomaduro/collision": "^7.10.0|^8.1.1",
"nunomaduro/collision": "^7.10.0|^8.3.0",
"nunomaduro/termwind": "^1.15.1|^2.0.1",
"pestphp/pest-plugin": "^2.1.1",
"pestphp/pest-plugin-arch": "^2.7.0",
"php": "^8.1.0",
"phpunit/phpunit": "^10.5.13"
"phpunit/phpunit": "^10.5.17"
},
"conflict": {
"phpunit/phpunit": ">10.5.13",
"phpunit/phpunit": ">10.5.17",
"sebastian/exporter": "<5.1.0",
"webmozart/assert": "<1.11.0"
},
"require-dev": {
"pestphp/pest-dev-tools": "^2.16.0",
"pestphp/pest-plugin-type-coverage": "^2.8.1",
"symfony/process": "^6.4.0|^7.0.4"
"pestphp/pest-plugin-type-coverage": "^2.8.5",
"symfony/process": "^6.4.0|^7.1.3"
},
"bin": [
"bin/pest"
@@ -3055,7 +3055,7 @@
],
"support": {
"issues": "https://github.com/pestphp/pest/issues",
"source": "https://github.com/pestphp/pest/tree/v2.34.4"
"source": "https://github.com/pestphp/pest/tree/v2.35.0"
},
"funding": [
{
@@ -3067,7 +3067,7 @@
"type": "github"
}
],
"time": "2024-03-14T19:44:18+00:00"
"time": "2024-08-02T10:57:29+00:00"
},
{
"name": "pestphp/pest-plugin",
@@ -3390,12 +3390,12 @@
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39"
"reference": "60741fe3871f40e44ca10a28ce85d08b7ed841cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39",
"reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/60741fe3871f40e44ca10a28ce85d08b7ed841cd",
"reference": "60741fe3871f40e44ca10a28ce85d08b7ed841cd",
"shasum": ""
},
"require": {
@@ -3447,7 +3447,7 @@
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x"
},
"time": "2024-05-21T06:14:15+00:00"
"time": "2024-08-14T20:00:37+00:00"
},
{
"name": "phpdocumentor/type-resolver",
@@ -3880,16 +3880,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.5.13",
"version": "10.5.17",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7"
"reference": "c1f736a473d21957ead7e94fcc029f571895abf5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/20a63fc1c6db29b15da3bd02d4b6cf59900088a7",
"reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c1f736a473d21957ead7e94fcc029f571895abf5",
"reference": "c1f736a473d21957ead7e94fcc029f571895abf5",
"shasum": ""
},
"require": {
@@ -3961,7 +3961,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.13"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.17"
},
"funding": [
{
@@ -3977,7 +3977,7 @@
"type": "tidelift"
}
],
"time": "2024-03-12T15:37:41+00:00"
"time": "2024-04-05T04:39:01+00:00"
},
{
"name": "psr/log",
@@ -4959,12 +4959,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "3c43d7ec3eb45e0eac993e86bee2905bfee5246b"
"reference": "cc2dd55c49e04b10cec4aa6e47e099a2d1656845"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/3c43d7ec3eb45e0eac993e86bee2905bfee5246b",
"reference": "3c43d7ec3eb45e0eac993e86bee2905bfee5246b",
"url": "https://api.github.com/repos/symfony/console/zipball/cc2dd55c49e04b10cec4aa6e47e099a2d1656845",
"reference": "cc2dd55c49e04b10cec4aa6e47e099a2d1656845",
"shasum": ""
},
"require": {
@@ -5044,7 +5044,7 @@
"type": "tidelift"
}
],
"time": "2024-08-13T14:29:53+00:00"
"time": "2024-08-15T22:49:18+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -5759,7 +5759,8 @@
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
"laravel/pint": 20
"laravel/pint": 20,
"larapack/dd": 20
},
"prefer-stable": false,
"prefer-lowest": false,
+7 -7
View File
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.4/phpunit.xsd"
>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
</testsuites>
<source>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
<testsuites>
<testsuite name="Laravel Cart Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
+9 -9
View File
@@ -1,12 +1,12 @@
{
"preset": "laravel",
"rules": {
"concat_space": {
"spacing": "one"
},
"class_attributes_separation": false,
"ordered_imports": {
"sort_algorithm": "length"
}
"preset": "laravel",
"rules": {
"concat_space": {
"spacing": "one"
},
"class_attributes_separation": false,
"ordered_imports": {
"sort_algorithm": "length"
}
}
}
+55 -54
View File
@@ -131,20 +131,20 @@ public function has($itemId)
* add item to the cart, it can be an array or multi dimensional array
*
* @param string|array $id
* @param string $name
* @param float $price
* @param int $quantity
* @param array $attributes
* @param CartCondition|array $conditions
* @param string $associatedModel
* @return $this
*
* @throws InvalidItemException
*/
public function add($id, $name = null, $price = null, $quantity = null, $attributes = [], $conditions = [], $associatedModel = null)
{
// if the first argument is an array,
// we will need to call add again
public function add(
string|int|array $id,
?string $name = null,
?float $price = null,
int|float|null $quantity = null,
array $attributes = [],
array|CartCondition $conditions = [],
?string $associatedModel = null
): self {
// check if its an array of items
if (is_array($id)) {
// the first argument is an array, now we will need to check if it is a multi dimensional
// array, if so, we will iterate through each item and call add again
@@ -153,7 +153,7 @@ public function add($id, $name = null, $price = null, $quantity = null, $attribu
$this->add(
$item['id'],
$item['name'],
$item['price'],
isset($item['price']) ? $item['price'] : null,
$item['quantity'],
Helpers::issetAndHasValueOrAssignDefault($item['attributes'], []),
Helpers::issetAndHasValueOrAssignDefault($item['conditions'], []),
@@ -164,7 +164,7 @@ public function add($id, $name = null, $price = null, $quantity = null, $attribu
$this->add(
$id['id'],
$id['name'],
$id['price'],
isset($id['price']) ? $id['price'] : null,
$id['quantity'],
Helpers::issetAndHasValueOrAssignDefault($id['attributes'], []),
Helpers::issetAndHasValueOrAssignDefault($id['conditions'], []),
@@ -192,15 +192,13 @@ public function add($id, $name = null, $price = null, $quantity = null, $attribu
$item = $this->validate($data);
// get the cart
$cart = $this->getContent();
$inCart = $this->get($id);
// if the item is already in the cart we will just update it
if ($cart->has($id)) {
if ($inCart) {
$this->update($id, $item);
} else {
$this->addRow($id, $item);
$this->addItem($id, $item);
}
$this->currentItemId = $id;
@@ -209,15 +207,9 @@ public function add($id, $name = null, $price = null, $quantity = null, $attribu
}
/**
* update a cart
*
* @param array $data
*
* the $data will be an associative array, you don't need to pass all the data, only the key value
* of the item you want to update on it
* @return bool
* Update a cart item with whatever data is passed in
*/
public function update($id, $data)
public function update(int|string $id, array $data): bool
{
if ($this->fireEvent('updating', $data) === false) {
return false;
@@ -225,21 +217,13 @@ public function update($id, $data)
$cart = $this->getContent();
$item = $cart->pull($id);
$item = $cart->get($id);
foreach ($data as $key => $value) {
// if the key is currently "quantity" we will need to check if an arithmetic
// symbol is present so we can decide if the update of quantity is being added
// or being reduced.
if ($key == 'quantity') {
// we will check if quantity value provided is array,
// if it is, we will need to check if a key "relative" is set
// and we will evaluate its value if true or false,
// this tells us how to treat the quantity value if it should be updated
// relatively to its current quantity value or just totally replace the value
if (is_array($value)) {
if (isset($value['relative'])) {
if ((bool) $value['relative']) {
if (isset($value['relative']) && $value['relative'] === true) {
$item = $this->updateQuantityRelative($item, $key, $value['value']);
} else {
$item = $this->updateQuantityNotRelative($item, $key, $value['value']);
@@ -255,6 +239,7 @@ public function update($id, $data)
}
}
// update the item in the cart
$cart->put($id, $item);
$this->save($cart);
@@ -353,14 +338,11 @@ public function clear()
}
/**
* add a condition on the cart
*
* @param CartCondition|array $condition
* @return $this
* Adds a condition to the cart
*
* @throws InvalidConditionException
*/
public function condition(CartCondition $condition)
public function condition(array|CartCondition $condition): self
{
if (is_array($condition)) {
foreach ($condition as $c) {
@@ -533,13 +515,9 @@ public function clearItemConditions($itemId)
}
/**
* clears all conditions on a cart,
* this does not remove conditions that has been added specifically to an item/product.
* If you wish to remove a specific condition to a product, you may use the method: removeItemCondition($itemId, $conditionName)
*
* @return void
* Clear cart conditions (this does not clear item conditions)
*/
public function clearCartConditions()
public function clearCartConditions(): void
{
$this->session->put(
$this->sessionKeyCartConditions,
@@ -547,6 +525,20 @@ public function clearCartConditions()
);
}
/**
* Clear all conditions from all items and the cart
*/
public function clearAllConditions(): void
{
$cart = $this->getContent();
$cart->each(function ($item) {
$this->clearItemConditions($item->id);
});
$this->clearCartConditions();
}
/**
* get cart sub total without conditions
*
@@ -706,15 +698,24 @@ protected function validate($item)
{
$rules = [
'id' => 'required',
'price' => 'required|numeric',
'quantity' => 'required|numeric|min:0.1',
'name' => 'required',
'quantity' => 'required|numeric|min:0.1',
];
$validator = CartItemValidator::make($item, $rules);
if ($validator->fails()) {
throw new InvalidItemException($validator->messages()->first());
if ($validator->errors()->first('id') === 'validation.required') {
throw new InvalidItemException('The id field is required.');
}
if ($validator->errors()->first('name') === 'validation.required') {
throw new InvalidItemException('The name field is required.');
}
if ($validator->errors()->first('quantity') === 'validation.min.numeric') {
throw new InvalidItemException('The quantity must be at least 0.1.');
}
}
return $item;
@@ -725,7 +726,7 @@ protected function validate($item)
*
* @return bool
*/
protected function addRow($id, $item)
protected function addItem($id, $item)
{
if ($this->fireEvent('adding', $item) === false) {
return false;
@@ -792,7 +793,7 @@ protected function itemHasConditions($item)
protected function updateQuantityRelative($item, $key, $value)
{
if (preg_match('/\-/', $value) == 1) {
$value = (int) str_replace('-', '', $value);
$value = (float) str_replace('-', '', $value);
// we will not allowed to reduced quantity to 0, so if the given value
// would result to item quantity of 0, we will not do it.
@@ -800,9 +801,9 @@ protected function updateQuantityRelative($item, $key, $value)
$item[$key] -= $value;
}
} elseif (preg_match('/\+/', $value) == 1) {
$item[$key] += (int) str_replace('+', '', $value);
$item[$key] += (float) str_replace('+', '', $value);
} else {
$item[$key] += (int) $value;
$item[$key] += (float) $value;
}
return $item;
@@ -815,7 +816,7 @@ protected function updateQuantityRelative($item, $key, $value)
*/
protected function updateQuantityNotRelative($item, $key, $value)
{
$item[$key] = (int) $value;
$item[$key] = (float) $value;
return $item;
}
+1 -3
View File
@@ -4,6 +4,4 @@
use Illuminate\Support\Collection;
class CartCollection extends Collection
{
}
class CartCollection extends Collection {}
-6
View File
@@ -145,12 +145,6 @@ public function getCalculatedValue($totalOrSubTotalOrPrice = null): int|float
*/
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
+1 -3
View File
@@ -2,6 +2,4 @@
namespace Wearepixel\Cart\Exceptions;
class UnknownModelException extends \Exception
{
}
class UnknownModelException extends \Exception {}
+6 -10
View File
@@ -7,7 +7,7 @@ class Helpers
/**
* Normalize prices
*/
public static function normalizePrice($price): float
public static function normalizePrice($price): ?float
{
return (is_string($price)) ? floatval($price) : $price;
}
@@ -25,15 +25,10 @@ public static function roundMode($config): int
}
/**
* check if array is multi dimensional array
* This will only check the first element of the array if it is still an array
* to decide that it is a multi dimensional, if you want to check the array strictly
* with all on its element, flag the second argument as true
*
* @param bool $recursive
* @return bool
* Check if array is multi dimensional array
* This will only check if the first element of the array is an array
*/
public static function isMultiArray($array, $recursive = false)
public static function isMultiArray(array $array, bool $recursive = false): bool
{
if ($recursive) {
return (count($array) == count($array, COUNT_RECURSIVE)) ? false : true;
@@ -45,8 +40,9 @@ public static function isMultiArray($array, $recursive = false)
return false;
}
}
}
return false;
}
/**
+2 -3
View File
@@ -1,8 +1,7 @@
<?php
return [
'format_numbers' => false,
'format_numbers' => true,
'decimals' => 2,
'dec_point' => '.',
'thousands_sep' => ',',
'round_mode' => 'up',
];
+194 -66
View File
@@ -791,6 +791,130 @@
expect($couponDiscount->getCalculatedValue())->toEqual(200.0, 'Coupon Discount value should be 200.0');
expect($giftCard->getCalculatedValue())->toEqual(0, 'Gift Card value should be 0');
});
test('can be cleared', function () {
$siteWideDiscount = new CartCondition([
'name' => 'Site Wide Discount',
'type' => 'discount',
'target' => 'total',
'value' => '-5%',
]);
$giftCard = new CartCondition([
'name' => 'Gift Card',
'type' => 'gift_card',
'target' => 'total',
'value' => '-25',
]);
$item = [
'id' => 1,
'name' => 'Backpack',
'price' => 168.72,
'quantity' => 1,
'attributes' => [],
];
$this->cart->add($item);
$this->cart->condition([$siteWideDiscount, $giftCard]);
expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions');
$this->cart->clearCartConditions();
expect($this->cart->getConditions()->count())->toEqual(0, 'Cart should have no conditions now');
});
test('calculate the subtotal correctly when one condition makes it $0, and the other adds $10', 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
$couponCode = new CartCondition([
'name' => 'Coupon code',
'type' => 'discount',
'target' => 'subtotal',
'order' => '1',
'value' => '-100%',
]);
$shipping = new CartCondition([
'name' => 'Shipping',
'type' => 'shipping',
'target' => 'subtotal',
'order' => '2',
'value' => '10',
]);
$this->cart->condition($couponCode);
$this->cart->condition($shipping);
expect($this->cart->getSubTotal())->toEqual(10.00, 'Cart should have sub total of 10.00');
expect($this->cart->getTotal())->toEqual(10.00, 'Cart should have a total of 10.00');
});
test('calculate the subtotal correctly when one condition makes it $0 (with dollars), and the other adds $10', 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
$couponCode = new CartCondition([
'name' => 'Coupon code',
'type' => 'discount',
'target' => 'subtotal',
'order' => '1',
'value' => '-137.24',
]);
$shipping = new CartCondition([
'name' => 'Shipping',
'type' => 'shipping',
'target' => 'subtotal',
'order' => '2',
'value' => '10',
]);
$this->cart->condition($couponCode);
$this->cart->condition($shipping);
expect($this->cart->getSubTotal())->toEqual(10.00, 'Cart should have sub total of 10.00');
expect($this->cart->getTotal())->toEqual(10.00, 'Cart should have a total of 10.00');
});
});
describe('item level conditions', function () {
@@ -890,113 +1014,72 @@
expect($this->cart->getTotal())->toEqual(411.77, 'Cart total with 1 item should be 411.77');
});
test('add item condition', function () {
test('can add item condition to an item that already has conditions', function () {
$items = [
[
'id' => 456,
'name' => 'Sample Item 1',
'id' => 1,
'name' => 'Children\'s Bookcase',
'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',
$discount = new CartCondition([
'name' => '$25 Off',
'type' => 'promo',
'value' => '-25',
]);
$coupon101 = new CartCondition([
'name' => 'COUPON 101',
$childrenDiscount = new CartCondition([
'name' => '5% Off Childrens Items',
'type' => 'coupon',
'value' => '-5%',
]);
$item = [
'id' => 456,
'name' => 'Sample Item 1',
'price' => 100,
'id' => 2,
'name' => 'Children\'s Pack of Books',
'price' => 31.22,
'quantity' => 1,
'attributes' => [],
'conditions' => [$itemCondition2],
'conditions' => [$discount],
];
$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');
expect($this->cart->get(2)['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);
$this->cart->addItemCondition(2, $childrenDiscount);
expect($this->cart->get($item['id'])['conditions'])->toHaveCount(2, 'Item should have 2 conditions');
expect($this->cart->get(2)['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' => [],
],
test('will not take the price below 0', function () {
$item = [
'id' => 1,
'name' => 'Picture Frame',
'price' => 22.50,
'quantity' => 1,
'attributes' => [],
];
$this->cart->add($items);
$condition = new CartCondition([
'name' => 'Substract amount but prevent negative value',
'name' => '$25 Discount',
'type' => 'promo',
'value' => '-25',
]);
$item = [
'id' => 789,
'name' => 'Sample Item 1',
'price' => 20,
'quantity' => 1,
'attributes' => [],
'conditions' => [
$condition,
],
];
$this->cart->add($item);
$this->cart->addItemCondition(1, $condition);
// 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.");
expect($this->cart->get(1)->getPriceSumWithConditions())->toEqual(0.00, "The item's price should be prevented from going below zero.");
});
// test('get cart condition by condition name', function () {
@@ -1475,3 +1558,48 @@
// });
});
describe('conditions', function () {
test('can be completely cleared', function () {
$siteWideDiscount = new CartCondition([
'name' => 'Site Wide Discount',
'type' => 'discount',
'target' => 'total',
'value' => '-5%',
]);
$giftCard = new CartCondition([
'name' => 'Gift Card',
'type' => 'gift_card',
'target' => 'total',
'value' => '-25',
]);
$itemCondition = new CartCondition([
'name' => 'Item Discount',
'type' => 'discount',
'value' => '-5%',
]);
$item = [
'id' => 1,
'name' => 'Backpack',
'price' => 168.72,
'quantity' => 1,
'attributes' => [],
'conditions' => [$itemCondition],
];
$this->cart->add($item);
$this->cart->condition([$siteWideDiscount, $giftCard]);
expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions');
expect($this->cart->get(1)['conditions'])->toHaveCount(1, 'Cart should have one condition');
$this->cart->clearAllConditions();
expect($this->cart->getConditions()->count())->toEqual(0, 'Cart should have no conditions now');
expect($this->cart->get(1)['conditions'])->toHaveCount(0, 'Cart items should have no conditions now');
});
});
+236 -87
View File
@@ -49,6 +49,34 @@
expect($this->cart->getTotal())->toEqual(0, 'Total should be 0');
});
test('can add an item an array with no price', function () {
$this->cart->add([
'id' => 455,
'name' => 'Sample Item',
'quantity' => 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');
});
test('can add an item without attributes', function () {
$item = [
'id' => 1,
'name' => 'Pocket Shorts',
'price' => 67.99,
'quantity' => 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->getTotal())->toEqual(135.98, 'Cart total should be 135.98');
});
test('can add items as array', function () {
$item = [
'id' => 456,
@@ -65,82 +93,227 @@
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 () {
$items = [
[
'id' => 456,
'name' => 'Sample Item 1',
test('can add multiple items with an array', function () {
$items = [
[
'id' => 1,
'name' => 'Half Button Open Collar',
'price' => 71.99,
'quantity' => 1,
'attributes' => [],
],
[
'id' => 2,
'name' => 'Oli Crew Neck',
'price' => 59.99,
'quantity' => 1,
'attributes' => [],
],
[
'id' => 3,
'name' => 'Denim Shorts',
'price' => 69.99,
'quantity' => 3,
'attributes' => [],
],
];
$this->cart->add($items);
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
expect($this->cart->getContent()->toArray())->toHaveCount(3, 'Cart should have 3 items');
expect($this->cart->getTotal())->toEqual(341.95, 'Cart should have a total of 341.95');
});
test('cart item prices should be normalized when added to cart', function () {
// add a price in a string format should be converted to float
$this->cart->add(1, 'Book of Languages Item', '100.99', 2, []);
expect($this->cart->getContent()->first()['price'])->toBeFloat('Cart price should be a float');
});
test('can update a cart item with new attributes and it should be still instance of item attribute collection', function () {
$item = [
'id' => 1,
'name' => 'Leather Shows',
'price' => 67.99,
'quantity' => 4,
'attributes' => [],
],
[
'id' => 568,
'name' => 'Sample Item 2',
'price' => 69.25,
'quantity' => 4,
'attributes' => [],
],
[
'id' => 856,
'name' => 'Sample Item 3',
'price' => 50.25,
'quantity' => 4,
'attributes' => [],
],
];
'attributes' => [
'product_id' => '145',
'color' => 'red',
],
];
$this->cart->add($items);
$this->cart->add($item);
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
expect($this->cart->getContent()->toArray())->toHaveCount(3, 'Cart should have 3 items');
});
$item = $this->cart->get(1);
test('cart can add item without attributes', function () {
$item = [
'id' => 456,
'name' => 'Sample Item 1',
'price' => 67.99,
'quantity' => 4,
];
expect($item->attributes)->toBeInstanceOf('Wearepixel\\Cart\\ItemAttributeCollection');
$this->cart->add($item);
// now lets update the item with its new attributes
// when we get that item from cart, it should still be an instance of ItemAttributeCollection
$updatedItem = [
'attributes' => [
'product_id' => '145',
'color' => 'red',
],
];
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
});
$this->cart->update(1, $updatedItem);
test('cart update with attribute then attributes should be still instance of item attribute collection', function () {
$item = [
'id' => 456,
'name' => 'Sample Item 1',
'price' => 67.99,
'quantity' => 4,
'attributes' => [
'product_id' => '145',
'color' => 'red',
],
];
$this->cart->add($item);
expect($item->attributes)->toBeInstanceOf('Wearepixel\\Cart\\ItemAttributeCollection');
});
// lets get the attribute and prove first its an instance of
// ItemAttributeCollection
$item = $this->cart->get(456);
test('item orders are preserved by the order they are added', function () {
$items = [
[
'id' => 50,
'name' => 'Leather Shows',
'price' => 67.99,
'quantity' => 4,
'attributes' => [
'product_id' => '145',
'color' => 'red',
],
],
[
'id' => 2,
'name' => 'Oli Crew Neck',
'price' => 59.99,
'quantity' => 1,
'attributes' => [],
],
];
expect($item->attributes)->toBeInstanceOf('Wearepixel\Cart\ItemAttributeCollection');
$this->cart->add($items);
// now lets update the item with its new attributes
// when we get that item from cart, it should still be an instance of ItemAttributeCollection
$updatedItem = [
'attributes' => [
'product_id' => '145',
'color' => 'red',
],
];
$this->cart->update(456, $updatedItem);
$items = $this->cart->getContent();
expect($item->attributes)->toBeInstanceOf('Wearepixel\Cart\ItemAttributeCollection');
expect($items->toArray())->toHaveCount(2, 'Cart should have 2 items');
expect($items->first()->id)->toEqual(50, 'First item should have ID of 1');
expect($items->last()->id)->toEqual(2, 'Last item should have ID of 2');
});
test('item orders are preserved by the order they are added even when items are updated', function () {
$items = [
[
'id' => 50,
'name' => 'Leather Shows',
'price' => 67.99,
'quantity' => 4,
'attributes' => [
'product_id' => '145',
'color' => 'red',
],
],
[
'id' => 2,
'name' => 'Oli Crew Neck',
'price' => 59.99,
'quantity' => 1,
'attributes' => [],
],
];
$this->cart->add($items);
$this->cart->update(50, ['name' => 'Updated Name']);
$items = $this->cart->getContent();
expect($items->toArray())->toHaveCount(2, 'Cart should have 2 items');
expect($items->first()->id)->toEqual(50, 'First item should have ID of 1');
expect($items->last()->id)->toEqual(2, 'Last item should have ID of 2');
});
test('can update existing items', function () {
$items = [
[
'id' => 1,
'name' => 'Magic The Gathering Card Pack',
'price' => 12.99,
'quantity' => 3,
'attributes' => [],
],
[
'id' => 2,
'name' => 'Magic The Gathering Card Case',
'price' => 39.99,
'quantity' => 1,
'attributes' => [],
],
];
$this->cart->add($items);
$item = $this->cart->get(1);
expect($item['name'])->toEqual('Magic The Gathering Card Pack', 'Item name should be "Magic The Gathering Card Pack"');
expect($item['price'])->toEqual(12.99, 'Item price should be "12.99"');
expect($item['quantity'])->toEqual(3, 'Item quantity should be 3');
// when cart's item quantity is updated, the subtotal should be updated as well
$this->cart->update(1, [
'name' => 'Magic The Gathering Card Packs',
'quantity' => 2,
'price' => 10.20,
]);
$item = $this->cart->get(1);
expect($item['name'])->toEqual('Magic The Gathering Card Packs', 'Item name should be "Magic The Gathering Card Packs"');
expect($item['price'])->toEqual(10.20, 'Item price should be 10.20');
expect($item['quantity'])->toEqual(5, 'Item quantity should be 2');
});
test('can update existing item quanties with decimals', function () {
$items = [
'id' => 1,
'name' => 'Fertilizer',
'price' => 30,
'quantity' => 7.8,
'attributes' => [
'unit' => 'kilogram',
],
];
$this->cart->add($items);
$item = $this->cart->get(1);
expect($item['quantity'])->toEqual(7.8, 'Item quantity should be 7.8');
// when cart's item quantity is updated, the subtotal should be updated as well
$this->cart->update(1, [
'quantity' => [
'relative' => true,
'value' => 2.1,
],
]);
$item = $this->cart->get(1);
expect($item['quantity'])->toEqual(9.9, 'Item quantity should be 9.9');
});
test('should throw an exception when no quantity is provided', function () {
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
$this->expectExceptionMessage('The quantity must be at least 0.1.');
$this->cart->add(455, 'Sample Item', 100.99, 0, []);
});
test('should throw an exception when no id is provided', function () {
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
$this->expectExceptionMessage('The id field is required.');
$this->cart->add('', 'Sample Item', 100.99, 2, []);
});
test('should throw an exception when no name is provided', function () {
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
$this->expectExceptionMessage('The name field is required.');
$this->cart->add(523, '', 100.99, 2, []);
});
});
test('cart items attributes', function () {
@@ -233,13 +406,6 @@
expect($item['quantity'])->toEqual(5, 'Item quantity should be 5');
});
test('item price should be normalized when added to cart', function () {
// add a price in a string format should be converted to float
$this->cart->add(455, 'Sample Item', '100.99', 2, []);
expect($this->cart->getContent()->first()['price'])->toBeFloat('Cart price should be a float');
});
test('it removes an item on cart by item id', function () {
$items = [
[
@@ -342,8 +508,6 @@
});
test('sub total when item quantity is updated by reduced', function () {
// $this->cart->setConfig(['format_numbers' => true, 'decimals' => 2, 'dec_point' => '.', 'thousands_sep' => ',']);
$items = [
[
'id' => 456,
@@ -409,21 +573,6 @@
expect($item['quantity'])->toEqual(3, 'Item quantity of with item ID of 456 should now be reduced to 2');
});
test('should throw exception when provided invalid values scenario one', function () {
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
$this->cart->add(455, 'Sample Item', 100.99, 0, []);
});
test('should throw exception when provided invalid values scenario two', function () {
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
$this->cart->add('', 'Sample Item', 100.99, 2, []);
});
test('should throw exception when provided invalid values scenario three', function () {
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
$this->cart->add(523, '', 100.99, 2, []);
});
test('clearing cart', function () {
$items = [
[