diff --git a/CHANGES.md b/CHANGES.md index 9c38282..1051b2e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased +- Added README.md documenting the cart API and how to run the checks. +- Added itemCount(items), which returns the total quantity across all items, ignoring quantities of 0 or less. +- Added discount(items, percent), which returns the total including tax after taking a percentage off the subtotal. ## 1.2.0 - total() rounds through roundCents(). diff --git a/README.md b/README.md new file mode 100644 index 0000000..436cc2a --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# cart-app + +Shopping cart library. Pure functions that take a list of items (`{ sku, price, qty }`) and return numbers. Money is in dollars, rounded to cents. + +## API (`src/lib/cart.js`) + +- `subtotal(items)`: returns the sum of price times quantity, before discounts and tax. +- `total(items)`: returns the cart total in dollars including tax, rounded to cents. +- `itemCount(items)`: returns the total quantity across all items, ignoring quantities of 0 or less. +- `discount(items, percent)`: returns the total including tax, after taking `percent` off the subtotal before tax. + +## Running the checks + +- `npm test` runs the unit tests. +- `npm run check` runs lint and then the tests. Run this before finishing a change. diff --git a/src/lib/cart.js b/src/lib/cart.js index 385fd69..46525f1 100644 --- a/src/lib/cart.js +++ b/src/lib/cart.js @@ -8,3 +8,14 @@ export function subtotal(items) { export function total(items) { return roundCents(subtotal(items) * (1 + TAX_RATE)); } + +/** Returns the total quantity across all items, ignoring quantities of 0 or less. */ +export function itemCount(items) { + return items.reduce((sum, item) => (item.qty > 0 ? sum + item.qty : sum), 0); +} + +/** Returns the cart total in dollars including tax, after taking percent off the subtotal. */ +export function discount(items, percent) { + const discounted = subtotal(items) * (1 - percent / 100); + return roundCents(discounted * (1 + TAX_RATE)); +} diff --git a/tests/lib/cart.test.js b/tests/lib/cart.test.js index 7a9eab3..6f777a7 100644 --- a/tests/lib/cart.test.js +++ b/tests/lib/cart.test.js @@ -1,6 +1,6 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { subtotal, total } from '../../src/lib/cart.js'; +import { discount, itemCount, subtotal, total } from '../../src/lib/cart.js'; const items = [{ price: 10, qty: 2 }, { price: 5, qty: 1 }]; @@ -11,3 +11,31 @@ test('subtotal adds price times quantity', () => { test('total applies tax and rounds to cents', () => { assert.equal(total(items), 27); }); + +test('itemCount adds quantities across all items', () => { + assert.equal(itemCount(items), 3); +}); + +test('itemCount is 0 for an empty cart', () => { + assert.equal(itemCount([]), 0); +}); + +test('itemCount ignores zero and negative quantities', () => { + assert.equal(itemCount([{ price: 1, qty: 0 }, { price: 1, qty: -2 }, { price: 1, qty: 4 }]), 4); +}); + +test('discount takes the percentage off the subtotal before applying tax', () => { + assert.equal(discount(items, 10), 24.3); +}); + +test('discount of 0 percent equals the regular total', () => { + assert.equal(discount(items, 0), total(items)); +}); + +test('discount of 100 percent is free', () => { + assert.equal(discount(items, 100), 0); +}); + +test('discount of an empty cart is 0', () => { + assert.equal(discount([], 20), 0); +});