Skip to main content

Cart decision tables

This page documents snappycart behaviour using decision tables.

It is designed for contributors who prefer rule-based test design over state-based modelling. Instead of focusing on state transitions first, this page focuses on conditions and expected outcomes.

Each decision table answers one core question:

given these conditions, what should happen?

Why decision tables fit snappycart

snappycart has a compact set of cart rules:

  • adding an item may create a new line or merge with an existing line
  • incrementing changes quantity only for existing lines
  • decrementing may reduce quantity or remove a line
  • clearing resets cart data
  • drawer visibility changes independently from cart data
  • invalid actions must not corrupt totals or item structure

That makes decision tables a strong fit.

How to read the tables

Each table contains:

  • conditions in the upper rows
  • actions in the lower rows
  • rule columns from left to right

To use a table:

  1. identify the event being tested
  2. evaluate the current conditions
  3. match those conditions to one rule column
  4. assert the actions marked with Y

Legend

SymbolMeaning
YYes, this condition is true
NNo, this condition is false
-This condition does not matter for this rule

Contract assumptions

The tables below assume the following intended behaviour:

  • items are matched by product.id
  • adding the same product merges into the existing line
  • decrement from quantity 1 removes the line item
  • clear() empties cart data
  • drawer visibility is controlled separately from cart data
  • invalid quantity input must not create a broken cart state
  • repeated actions must not corrupt totals or cart structure

Decision table 1: Add item behaviour

This table defines what happens when addItem() is triggered.

Conditions / RulesR1R2R3R4
Product payload is validYYNN
Product already exists in cartNY--
Quantity input is valid positive integerYY-N
Actions
Create new line itemYNNN
Increase existing line quantityNYNN
Recalculate totalItemsYYNN
Recalculate subtotalYYNN
Reject request safelyNNYY
Keep cart structure unchangedNNYY

Rule notes

  • R1: a valid new product creates a new line item
  • R2: a valid existing product increases quantity on the same line
  • R3: an invalid product payload is rejected safely
  • R4: an invalid quantity input is rejected safely

Visual reference

Add first item

This clip shows the rule where a valid new product creates the first line item in the cart.

Add same item and merge quantity

This clip shows the rule where adding the same product increases quantity on the existing line instead of creating a duplicate row.

Add different item and create a second line

This clip shows the rule where a different product creates an additional line item.


Decision table 2: Increment behaviour

This table defines what happens when increment() is triggered.

Conditions / RulesR1R2
Line item existsYN
Actions
Increase quantity by 1YN
Recalculate totalItemsYN
Recalculate subtotalYN
Reject request safelyNY
Keep cart structure unchangedNY

Rule notes

  • R1: increment works only for an existing line item
  • R2: increment on a missing line item must be a safe no-op or rejected safely

Decision table 3: Decrement behaviour

This table defines what happens when decrement() is triggered.

Conditions / RulesR1R2R3
Line item existsYYN
Current quantity is greater than 1YN-
Actions
Decrease quantity by 1YNN
Remove line itemNYN
Recalculate totalItemsYYN
Recalculate subtotalYYN
Reject request safelyNNY
Keep cart structure unchangedNNY

Rule notes

  • R1: quantity greater than 1 is reduced by one
  • R2: quantity equal to 1 removes the line item
  • R3: decrement on a missing line item must not corrupt the cart

Visual reference

Decrement quantity back to one

This clip shows the rule where decrement reduces quantity from a value greater than 1 back to 1.

Remove the last line item

This clip shows the rule where decrement or remove action clears the last remaining line item from the cart.


Decision table 4: Set quantity behaviour

This table defines what happens when setQuantity() is triggered.

Conditions / RulesR1R2R3R4R5
Line item existsYYYYN
New quantity is positive integer > 1YNNN-
New quantity is exactly 1NYNN-
New quantity is 0 or belowNNYN-
New quantity is invalid (NaN, decimal, non-numeric)NNNY-
Actions
Set quantity to provided valueYYNNN
Remove line itemNNYNN
Recalculate totalItemsYYYNN
Recalculate subtotalYYYNN
Reject request safelyNNNYY
Keep cart structure unchangedNNNYY

Rule notes

  • R1: a valid quantity above 1 sets the line quantity directly
  • R2: quantity 1 keeps the line as a single item
  • R3: zero or negative quantity removes the line item
  • R4: invalid quantity input must be rejected safely
  • R5: missing line item must not be mutated

Decision table 5: Remove item behaviour

This table defines what happens when removeItem() is triggered.

Conditions / RulesR1R2
Line item existsYN
Actions
Remove line itemYN
Recalculate totalItemsYN
Recalculate subtotalYN
Reject request safelyNY
Keep cart structure unchangedNY

Rule notes

  • R1: an existing line item is removed
  • R2: removing a missing line item must not corrupt the cart

Decision table 6: Clear cart behaviour

This table defines what happens when clear() is triggered.

Conditions / RulesR1R2
Cart has one or more line itemsYN
Actions
Remove all line itemsYN
Set totalItems to 0YY
Set subtotal to 0YY
Keep drawer visibility unchangedYY
Produce broken stateNN

Rule notes

  • R1: a non-empty cart is reset to empty
  • R2: clearing an already empty cart should still be safe and idempotent

Visual reference

This clip shows the rule where a non-empty cart is cleared and all derived values return to zero.


Decision table 7: Drawer open and close behaviour

This table defines what happens when the drawer UI is controlled.

Conditions / RulesR1R2R3R4R5R6
Drawer is currently openNYYYNY
Event is OPEN_DRAWERYNNNYN
Event is close button clickNYNNNN
Event is overlay clickNNYNNN
Event is Escape keyNNNYNN
Actions
Drawer becomes openYNNNYN
Drawer becomes closedNYYYNN
Cart data remains unchangedYYYYYY
No-op allowedNNNNYY

Rule notes

  • R1: opening a closed drawer shows the drawer
  • R2, R3, R4: standard close interactions close the drawer
  • R5: opening an already open drawer may be treated as a no-op
  • R6: closing a closed drawer may be treated as a no-op

Visual reference

This clip shows the drawer opening without mutating cart data.


Decision table 8: Empty state rendering

This table defines when the empty UI should be shown.

Conditions / RulesR1R2R3R4
Drawer is openYYNN
Cart has zero line itemsYNYN
Actions
Show empty state messageYNNN
Show line itemsNYNN
Hide drawer content from viewNNYY
Show clear cart actionNYNN

Rule notes

  • R1: empty state belongs to an open drawer with zero items
  • R2: a non-empty open drawer shows line items and relevant actions
  • R3, R4: when the drawer is closed, drawer content is not visible

Visual reference

Empty cart drawer open state

This screenshot shows the empty state rendered inside an open drawer.


Decision table 9: Badge rendering

This table defines the expected cart badge behaviour.

Conditions / RulesR1R2
totalItems equals 0YN
totalItems is greater than 0NY
Actions
Badge shows 0 or empty-cart representation defined by the UIYN
Badge shows current totalItems valueNY
Badge must match derived cart valueYY

Rule notes

This table does not force one specific visual design for zero-state badges. It only requires the badge output to remain consistent with the cart data contract.

Visual reference

Single line cart

Single line cart with quantity equal to one

Multi-line cart

Multi-line cart with drawer open

These screenshots show examples where visible cart output should remain consistent with derived totals.


Decision table 10: Derived values integrity

This table defines when totals must be recomputed.

Conditions / RulesR1R2R3
Cart mutation succeedsYNN
Request is rejected safelyNYN
UI-only event happensNNY
Actions
Recalculate totalItemsYNN
Recalculate subtotalYNN
Keep derived values unchangedNYY
Keep cart structure unchangedNYY

Rule notes

  • R1: successful cart mutations must update derived values
  • R2: rejected actions must not change totals
  • R3: UI-only events such as opening or closing the drawer must not change totals

How contributors should use this page

Use these tables to design tests at the right level.

For reducer unit tests

Focus on:

  • add, remove, increment, decrement, set quantity, clear
  • line item structure
  • derived values

For provider and hook integration tests

Focus on:

  • public behaviour through useCart()
  • items, totalItems, and subtotal
  • safe behaviour when actions are invalid

For component tests

Focus on:

  • drawer visibility
  • empty state rendering
  • visible item rows
  • clear cart visibility
  • badge output

For end-to-end smoke tests

Focus on:

  • happy path user flows
  • one representative path per important rule group
  • avoiding duplication of reducer-level rule coverage

How to convert a rule into a test

A practical flow is:

  1. choose one decision table
  2. pick one rule column
  3. set up the preconditions that satisfy that rule
  4. perform the event
  5. assert only the actions marked Y
  6. assert that non-selected actions did not happen

Coverage guidance

These decision tables are intended to make the rules explicit.

They are not a requirement to write one test per table row.

They are also not a requirement to duplicate the same rule across every testing layer.

The right goal is:

  • full rule coverage
  • minimal duplication
  • stable tests
  • clear ownership of behaviour

Summary

snappycart can be described effectively through decision tables because its behaviour is rule-driven.

This page gives contributors a compact way to answer the core question behind every cart test:

given these conditions, what should happen next?