Skip to content
Quality Assurance Labs

Sample QA report

This is what you get from us.

A real report from real test runs: automated, exploratory and accessibility testing of the checkout in our lab demo store, a small shop with bugs modelled on ones we often find in client stores. Client reports use the same format and stay confidential.

Download PDF (11 pages)

Report QAL-LAB-2026-11-02 · version 1.1 · prepared by QA Labs

Summary

Release recommendation

Do not release.

  • Shoppers can set their own price or make the total negative (QA-001, QA-005).
  • Declined cards and double clicks create orders that shouldn't exist (QA-002, QA-004).
  • The last item in stock can be sold twice during a rush (QA-003).

All P1 defects must be fixed and retested before release. The seven automated-test defects have since been fixed and verified; six lower-severity findings remain open.

Defects found

13: 2 critical, 3 high, 4 medium, 4 low

Found by

7 automated tests, 5 exploratory, 1 accessibility scan

Automated pass rate

1 / 8 (12.5%) → 8 / 8 after fixes

Stability

Each test run 10×: 70 / 70 failed before, 80 / 80 passed after

Test cases run

Eight automated checks, each tied to a business rule of the checkout. Every one was run 10 times to rule out timing flukes.

IDBusiness rule checkedFirst runRetest
TC-01A paid order is created and stock goes downPassPass (10/10)
TC-02The server ignores a price sent by the browserFail · QA-001Pass (10/10)
TC-03Zero, negative and fractional quantities are rejectedFail · QA-005Pass (10/10)
TC-04Only one coupon per orderFail · QA-006Pass (10/10)
TC-05Tax is charged on the discounted subtotalFail · QA-007Pass (10/10)
TC-06A declined card creates no order and keeps the stockFail · QA-002Pass (10/10)
TC-07Two shoppers cannot both buy the last unitFail · QA-003Pass (10/10)
TC-08Double-clicking Place order creates one orderFail · QA-004Pass (10/10)

Defects

Severity: how bad the impact is

Critical
Money, data or security is directly at risk. Blocks release.
High
A core flow breaks or produces wrong results for many users. Blocks release.
Medium
Wrong behaviour with a workaround, or limited reach. Fix in the current sprint.
Low
Cosmetic, clarity or best-practice issue. Schedule when convenient.

Priority: how soon to fix it

P1
Fix before release
P2
Fix this sprint
P3
Backlog

They are rated separately: QA-007 is medium severity but P1, because wrong tax is a legal risk.

IDDefectSeverityPriorityFound byStatus
QA-001The server accepts a price sent by the browserCriticalP1AutomatedFixed · verified
QA-002A declined card still creates an order and removes stockCriticalP1AutomatedFixed · verified
QA-003Two shoppers can both buy the last unitHighP1AutomatedFixed · verified
QA-004Double-clicking Place order creates two ordersHighP1AutomatedFixed · verified
QA-005Zero, negative and fractional quantities are acceptedHighP1AutomatedFixed · verified
QA-006Coupons stack without limitMediumP2AutomatedFixed · verified
QA-007Tax is charged before the discountMediumP1AutomatedFixed · verified
QA-008Add to cart crashes while the cart is still being createdMediumP2ExploratoryOpen
QA-009An unknown coupon is ignored without any messageMediumP2ExploratoryOpen
QA-010Ordering with an empty cart shows “Payment failed: empty_cart”LowP3ExploratoryOpen
QA-011A coupon can be applied to an empty cartLowP3ExploratoryOpen
QA-012Adding the same item twice creates two cart linesLowP3ExploratoryOpen
QA-013Page has no main landmarkLowP3AccessibilityOpen
QA-001CriticalP1Cart API · AutomatedFixed · verified

The server accepts a price sent by the browser

Steps to reproduce

  1. Create a cart.
  2. Add 1 × Cap ($24.00) with an extra field in the request: price = 0.01.
  3. Read the cart totals.

Reproducibility: Always (10/10)

Expected
Subtotal $24.00 (the catalogue price).
Actual
Subtotal $0.01, total $0.01.

Evidence

POST /api/carts/{cart}/items {"sku":"CAP","qty":1,"price":0.01}
→ 200 {"subtotal":0.01,"tax":0,"total":0.01}

Business impactAnyone who edits the request can buy any product for a cent. Direct revenue loss.

Suggested fixIgnore client-sent prices; always read the unit price from the catalogue on the server.

Caught bySecurity testing →

QA-002CriticalP1Checkout · AutomatedFixed · verified

A declined card still creates an order and removes stock

Steps to reproduce

  1. Add the last Logo tee (stock 1) to a cart.
  2. Check out with a card that is declined.
  3. List the cart's orders and read the stock.

Reproducibility: Always (10/10)

Expected
HTTP 402, no order, stock still 1.
Actual
402 returned, but a pending $21.64 order exists and stock is 0.

Evidence

POST /api/carts/{cart}/checkout {"paymentToken":"tok_declined"}
→ 402 {"error":"card_declined"}

GET /api/orders?cart={cart}
→ 200 [{"status":"pending","total":21.64}]

GET /api/products  (Logo tee)
→ 200 {"stock":0}

Business impactUnpaid orders reach fulfilment and items show as sold out when they are not.

Suggested fixCharge the card first; create the order and take stock only after the payment succeeds.

Caught byAPI testing →

QA-003HighP1Checkout · AutomatedFixed · verified

Two shoppers can both buy the last unit

Steps to reproduce

  1. Two carts each hold the last Logo tee.
  2. Both check out at the same moment.

Reproducibility: Always (10/10)

Expected
One order succeeds (201), the other is refused as out of stock (409).
Actual
Both returned 201 and stock fell to −1.

Evidence

POST /api/carts/{cart A}/checkout  (same moment)
→ 201 {"status":"paid","total":21.64}

POST /api/carts/{cart B}/checkout  (same moment)
→ 201 {"status":"paid","total":21.64}

GET /api/products  (Logo tee)
→ 200 {"stock":-1}

Business impactOverselling during busy sales: refunds, cancelled orders and unhappy customers.

Suggested fixRe-check and take stock inside a lock or database transaction at the moment of purchase.

Caught byPerformance testing →

QA-004HighP1Checkout UI · AutomatedFixed · verified

Double-clicking Place order creates two orders

Steps to reproduce

  1. Add a mug to the cart in the browser.
  2. Double-click Place order.
  3. List the orders.

Reproducibility: Always (10/10)

Expected
1 order.
Actual
2 paid orders of $13.53, so the shopper is charged twice.

Evidence

Browser: double-click Place order, then GET /api/orders
→ 200 [{"status":"paid","total":13.53},{"status":"paid","total":13.53}]

Business impactDouble charges, chargebacks and support tickets.

Suggested fixDisable the button after the first click and send an idempotency key the server enforces.

Caught byWeb application testing →

QA-005HighP1Cart API · AutomatedFixed · verified

Zero, negative and fractional quantities are accepted

Steps to reproduce

  1. Add a mug with quantity 0, then −3, then 2.5.

Reproducibility: Always (10/10)

Expected
Each request is refused with HTTP 400.
Actual
All accepted (200); the order total went negative.

Evidence

POST /api/carts/{cart}/items {"sku":"MUG","qty":0}
→ 200 {"subtotal":0,"total":0}

POST /api/carts/{cart}/items {"sku":"MUG","qty":-3}
→ 200 {"subtotal":-37.5,"total":-3.09}

POST /api/carts/{cart}/items {"sku":"MUG","qty":2.5}
→ 200 {"subtotal":-6.25,"total":-0.52}

Business impactShoppers can lower what they pay, and order data becomes unreliable.

Suggested fixValidate quantity on the server as a whole number from 1 to the per-order limit.

Caught byAPI testing →

Would your checkout pass these checks?

Get a free QA review →
QA-006MediumP2Promotions · AutomatedFixed · verified

Coupons stack without limit

Steps to reproduce

  1. Add 2 × Cap ($48.00).
  2. Apply SAVE10 (10% off).
  3. Apply WELCOME5 ($5 off).

Reproducibility: Always (10/10)

Expected
The second coupon is refused (409); discount stays $4.80.
Actual
Accepted (200); discount $9.80.

Evidence

POST /api/carts/{cart}/coupons {"code":"SAVE10"}
→ 200 {"coupons":["SAVE10"],"discount":4.8,"total":47.16}

POST /api/carts/{cart}/coupons {"code":"WELCOME5"}
→ 200 {"coupons":["SAVE10","WELCOME5"],"discount":9.8,"total":42.16}

Business impactMargins leak on every order that finds a second code.

Suggested fixEnforce one coupon per order on the server, including the same code twice.

Caught bySoftware QA testing →

QA-007MediumP1Totals · AutomatedFixed · verified

Tax is charged before the discount

Steps to reproduce

  1. Add 2 × Cap ($48.00) and apply SAVE10 (−$4.80 → $43.20).
  2. Read the tax at 8.25%.

Reproducibility: Always (10/10)

Expected
Tax $3.56 (8.25% of $43.20), total $46.76.
Actual
Tax $3.96 (8.25% of $48.00), total $47.16.

Evidence

GET /api/carts/{cart}  (2 × Cap, SAVE10)
→ 200 {"subtotal":48,"discount":4.8,"tax":3.96,"total":47.16}

Business impactEvery discounted order is over-taxed: customers are overcharged and tax filings are wrong. Medium severity, but P1 because of the legal exposure.

Suggested fixCalculate tax on the subtotal after discounts.

Caught byRegression testing →

QA-008MediumP2Storefront · ExploratoryOpen

Add to cart crashes while the cart is still being created

Steps to reproduce

  1. Throttle the network so creating the cart takes 1.5 s.
  2. Open the store and click Add to cart straight away.

Reproducibility: Always on a slow connection (cart request delayed 1.5 s)

Expected
The click waits for the cart (or the button is disabled until it exists), then the mug is added.
Actual
The request goes to /api/carts/undefined/items (404) and the page throws a JavaScript error; nothing is added and no message is shown.

Evidence

POST /api/carts/undefined/items
→ 404 Console: Cannot read properties of undefined (reading 'toFixed')

Business impactShoppers on mobile data tap Add to cart and nothing happens; some leave.

Suggested fixDisable Add to cart until the cart exists, and handle failed responses with a visible message.

Caught byWeb application testing →

QA-009MediumP2Promotions · ExploratoryOpen

An unknown coupon is ignored without any message

Steps to reproduce

  1. Add a mug.
  2. Enter BOGUS as the coupon and click Apply.

Reproducibility: Always

Expected
A clear, announced message such as “This coupon code isn’t valid”.
Actual
Nothing happens: the totals don't change and no message appears (the API answers 404 unknown_coupon). Also fails WCAG 3.3.1 Error Identification.
Demo store after applying the coupon BOGUS: the totals are unchanged and no error message is shown
Screenshot, Chromium 1280 px.

Business impactShoppers think the site is broken and abandon, or contact support.

Suggested fixShow the error next to the coupon field and announce it with role=“alert”.

Caught byUsability testing →

QA-010LowP3Checkout UI · ExploratoryOpen

Ordering with an empty cart shows “Payment failed: empty_cart”

Steps to reproduce

  1. Open the store.
  2. Click Place order without adding anything.

Reproducibility: Always

Expected
Place order is disabled until the cart has an item.
Actual
The button is enabled and the shopper sees a payment error with an internal code.
Demo store with an empty cart showing the message Payment failed: empty_cart
Screenshot, Chromium 1280 px.

Business impactConfusing, and it suggests a payment problem that doesn't exist.

Suggested fixDisable Place order for an empty cart and replace internal codes with plain messages.

Caught byUsability testing →

QA-011LowP3Promotions · ExploratoryOpen

A coupon can be applied to an empty cart

Steps to reproduce

  1. Create a cart.
  2. Apply SAVE10 before adding items.

Reproducibility: Always

Expected
Refused until the cart has items (or kept and re-validated at checkout).
Actual
Accepted (200).

Evidence

POST /api/carts/{empty cart}/coupons {"code":"SAVE10"}
→ 200 {"coupons":["SAVE10"],"subtotal":0,"total":0}

Business impactLow on its own, but it makes the stacking bug (QA-006) easier to abuse.

Suggested fixValidate coupons against the cart contents, and again at checkout.

Caught byAPI testing →

QA-012LowP3Cart API · ExploratoryOpen

Adding the same item twice creates two cart lines

Steps to reproduce

  1. Add a mug.
  2. Add a mug again.

Reproducibility: Always

Expected
One line: Enamel mug × 2.
Actual
Two separate lines of Enamel mug × 1.

Evidence

POST /api/carts/{cart}/items {"sku":"MUG","qty":1}  (twice)
→ 200 {"items":["MUG ×1","MUG ×1"]}

Business impactCluttered cart and harder quantity changes; can confuse stock limits.

Suggested fixMerge lines for the same product and increase the quantity.

Caught bySoftware QA testing →

QA-013LowP3Accessibility · AccessibilityOpen

Page has no main landmark

Steps to reproduce

  1. Run axe-core 4.14 on the store, empty and with an item in the cart.

Reproducibility: Always

Expected
Content sits inside landmarks (header, main), so screen-reader users can jump straight to it.
Actual
axe: landmark-one-main and region (best-practice, moderate) on the heading, product line, coupon field and totals.

Business impactSlower navigation for screen-reader users; not a WCAG A/AA failure on its own.

Suggested fixWrap the store content in <main> (and the title in <header>).

Caught byAccessibility testing →

Exploratory and accessibility testing

Exploratory session

Charter: explore the storefront as a real shopper would — empty states, wrong input, slow networks, keyboard only and a phone-sized screen — to find what the scripted tests don't cover.

Found: five defects (QA-008 to QA-012), including a crash on slow connections that no automated test caught.

Checked and working

  • Coupon codes with spaces and lower case (“ save10 ”) are accepted correctly
  • Keyboard only: Tab reaches Add to cart, Coupon, Apply and Place order in order, and Enter works
  • No horizontal scrolling at 375 px (phone width)
  • Order results are announced to screen readers (role=“status”)

Accessibility (WCAG 2.2 AA)

Automated: axe-core 4.14.0, empty cart and with an item. 0 WCAG A/AA violations, 19 rules passed, 2 best-practice issues (no main landmark, content outside landmarks: QA-013).

Manual: one WCAG failure that tools can't detect: an invalid coupon gives no error message (3.3.1 Error Identification, QA-009). Keyboard access and status announcements work.

Automated scanners can't detect every accessibility problem (QA-009 is an example), which is why we always pair them with manual checks.

Retest after fixes

The fixed build of the store resolves QA-001 to QA-007. We ran the full suite again, 10 times over, and every check passed. The tests stay in the store's pipeline, so these bugs can't quietly return. QA-008 to QA-013 are open and scheduled by priority.

First run

1 / 8 passed

Retest

8 / 8 passed

Repeated 10×

80 / 80 passed

Suite time

3.2 seconds

Scope, environment and sign-off

In scope

  • Cart, coupons, totals and checkout: API and browser
  • Exploratory testing, desktop and 375 px
  • Keyboard and automated accessibility checks

Not in scope

  • The real payment provider (a test stand-in was used)
  • Load testing (see our peak-sale load test)
  • Penetration testing, Safari, Firefox and real devices

Environment

Build
Lab demo store, buggy build, then fixed build
Date
2 November 2026
Browser
Chromium (Playwright 1.56)
Runtime
Node.js 22
Screens
1280 px desktop, 375 px phone
Accessibility
axe-core 4.14.0
Test data
3 products, 2 coupons, test cards (approved and declined)

Release criteria

  • No open Critical or High defects (met)
  • All automated checks pass, 10 runs in a row (met)
  • P2 items scheduled in the current sprint (pending)
  • Client acceptance (pending)

QAL-LAB-2026-11-02 · v1.1 · prepared by QA Labs · PDF

The services behind this report

Each finding above came from a different kind of testing. These are the same services we run for clients, alone or together.

Want a report like this for your product?

Start with a free QA risk review: a scoping call and a short written review of your release risks, with no obligation.