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?