- AC-001: Search box available trên mọi page (header). Shortcut Ctrl/Cmd+K mở search.
- AC-002: Debounce 300ms. Auto-suggest hiện sau 2 ký tự (max 8 suggestions).
- AC-003: Full-text search trên tên sản phẩm, mô tả, thương hiệu, model number.
- AC-004: Hỗ trợ tiếng Việt có dấu và không dấu: "iphone" tìm được "iPhone 15 Pro".
- AC-005: Kết quả hiện trong 2 giây với ≤ 100 concurrent users.
- AC-006: "Không tìm thấy kết quả cho '...'" → gợi ý 4 sản phẩm phổ biến.
- AC-007: Search history lưu local (max 10), hiện khi focus search box khi field trống.
ShopVN — E-commerce Website
Dự án thực hành web thương mại điện tử đầy đủ từ A đến Z theo chuẩn quốc tế — Requirements, Test Plan, Playwright E2E, API, Database, Cross-browser, Performance, Security, Accessibility và Go/No-Go Report.
| Item | Detail |
|---|---|
| Product | ShopVN v2.1 — Sprint 5 Build #142 |
| Frontend | Next.js 14 (App Router) + TypeScript + Tailwind CSS |
| Backend | Node.js 20 + Express 5 + TypeScript |
| Database | PostgreSQL 15 (primary), Redis 7 (cache + session), Elasticsearch 8 (product search) |
| Cloud | AWS (ECS, RDS Multi-AZ, ElastiCache, CloudFront CDN, S3 images) |
| Auth | NextAuth.js — JWT + Credentials + Google OAuth |
| Payment | Stripe (Visa/MC/Amex quốc tế), VNPay (nội địa Vietnam) |
| Search | Elasticsearch full-text search với Vietnamese analyzer |
| Test Environment | staging.shopvn.vn (AWS clone, seeded 5,000 products, 500 users) |
| CI/CD | GitHub Actions → Vercel (frontend) + AWS CodeDeploy (backend) |
| Sprint Goal | User có thể tìm kiếm sản phẩm, filter, thêm vào giỏ, checkout với VNPay/Stripe và nhận order confirmation email |
2. Elasticsearch sync: Product update trong DB → ES index update có ~5 giây lag → search test cần wait/poll.
3. Redis cart vs DB cart: Cart trong Redis, chỉ persist to DB khi checkout → nếu Redis restart, cart mất.
4. CloudFront cache: Product image update có thể cần 24h để propagate tới edge nodes → test với cache-bypass header.
5. Inventory race condition: Flash sale scenario — nhiều users mua cùng lúc → oversell risk.
Docker Compose. Mock payment. 100 products seeded. Dev only.
AWS clone. Stripe test + VNPay sandbox. 5,000 products. QA + Dev access.
Live. Post-deploy smoke only. Readonly monitoring access.
Sprint 5: 4 Epics, 9 User Stories. Reviewed theo chuẩn CCCTF (Completeness, Clarity, Consistency, Testability, Feasibility).
Epic 1: Product Catalog & Search
- AC-001: Filter options: Danh mục, Thương hiệu (multi-select checkbox), Khoảng giá (range slider), Đánh giá (≥ N sao), Tình trạng (Còn hàng / Tất cả).
- AC-002: Sort by: Phổ biến nhất, Mới nhất, Giá thấp → cao, Giá cao → thấp, Đánh giá cao nhất.
- AC-003: Filter state lưu trong URL params → shareable link và browser back button hoạt động.
- AC-004: Filter active hiện dưới dạng chips, có nút "×" xóa từng filter. "Xóa tất cả" button.
- AC-005: Số lượng kết quả cập nhật real-time khi thay đổi filter (không cần submit).
- AC-006: Trên mobile (≤768px): Filter hiện trong bottom drawer, không phải sidebar.
- AC-001: Product detail page có: ảnh gallery (zoom khi hover), tên, giá (gạch ngang giá gốc nếu có khuyến mãi), SKU, thương hiệu, mô tả, thông số kỹ thuật, đánh giá.
- AC-002: Variant selector (màu sắc, dung lượng) — chọn variant → giá và ảnh cập nhật.
- AC-003: Stock indicator: "Còn X sản phẩm" nếu ≤ 5, "Còn hàng" nếu > 5, "Hết hàng" nếu 0.
- AC-004: "Hết hàng" → nút "Thêm vào giỏ" disabled, hiện "Đăng ký thông báo".
- AC-005: Breadcrumb navigation: Trang chủ → Danh mục → Sản phẩm.
- AC-006: Structured data (JSON-LD) cho SEO: Product schema với price, rating, availability.
Epic 2: Shopping Cart
- AC-001: "Thêm vào giỏ" → mini cart slide-in từ phải, hiện sản phẩm vừa thêm và tổng giỏ hàng.
- AC-002: Cart icon trên header hiện badge với số lượng items (cập nhật real-time).
- AC-003: Tăng/giảm số lượng trong cart. Min = 1, max = stock available. Quantity 0 → xóa.
- AC-004: Cart persist cho logged-in user (server-side). Guest cart: localStorage, sync khi đăng nhập.
- AC-005: Giá tự động cập nhật nếu sản phẩm có promotion change. Hiện banner "Giá sản phẩm đã thay đổi".
- AC-006: "Hết hàng" sau khi thêm vào cart → hiện cảnh báo tại cart page, không cho checkout.
- AC-007: "Lưu để sau" (Save for later) — move item ra khỏi cart nhưng không xóa.
Epic 3: Checkout & Payment
- AC-001: Checkout flow 3 steps: (1) Địa chỉ giao hàng → (2) Phương thức vận chuyển → (3) Thanh toán.
- AC-002: Logged-in user: pre-fill địa chỉ đã lưu. Có thể chọn địa chỉ khác hoặc nhập mới.
- AC-003: Guest checkout được phép — chỉ cần email + địa chỉ, không bắt tạo account.
- AC-004: Địa chỉ Việt Nam: Tỉnh/Thành phố → Quận/Huyện → Phường/Xã (dropdown cascade từ API).
- AC-005: Tính phí vận chuyển real-time dựa trên địa chỉ giao hàng và trọng lượng đơn hàng.
- AC-006: Order summary sidebar sticky — visible khi scroll qua checkout steps.
- AC-001: Stripe Elements embedded trong checkout page (không redirect). Hỗ trợ Visa/MC/Amex/JCB.
- AC-002: 3D Secure authentication khi issuing bank yêu cầu.
- AC-003: Payment thành công → order status "Confirmed", email xác nhận gửi trong 60 giây.
- AC-004: Payment thất bại → hiện lý do cụ thể (card declined, insufficient funds, expired), không navigate khỏi checkout.
- AC-005: Idempotency key trên mỗi payment intent — double submit không charge 2 lần.
- AC-006: Không lưu card number trong DB của ShopVN — chỉ lưu Stripe PaymentMethod ID.
- AC-001: Chọn VNPay → redirect tới VNPay payment page (chuẩn VNPay integration).
- AC-002: VNPay hỗ trợ: QR Code (VNPay QR), ATM nội địa (96 banks), VISA/MC qua VNPay.
- AC-003: Sau khi thanh toán: VNPay redirect về /checkout/return?vnp_ResponseCode=00 (success) hoặc code khác (fail).
- AC-004: Server verify chữ ký HMAC-SHA512 từ VNPay webhook trước khi cập nhật order.
- AC-005: VNPay timeout (user không thanh toán trong 15 phút) → order status "Payment Expired", cart không bị clear.
- AC-001: Input mã coupon tại checkout. Real-time validation khi blur hoặc Enter.
- AC-002: Coupon types: % discount, fixed amount, free shipping, buy-X-get-Y.
- AC-003: Invalid coupon → error "Mã không hợp lệ hoặc đã hết hạn".
- AC-004: Coupon đã dùng (single-use) → error "Mã này đã được sử dụng".
- AC-005: Minimum order value: Coupon chỉ valid nếu subtotal ≥ threshold. Hiện: "Cần mua thêm X để sử dụng mã này".
- AC-006: Discount hiện trong order summary: gạch ngang subtotal + dòng "Giảm giá: -X₫".
US-507 AC-005: "cart không bị clear" — conflict với logic hiện tại khi order tạo, cart được clear. Cần clarify với dev: order được tạo trước rồi chờ payment, hay chỉ tạo khi payment thành công?
| Req ID | Acceptance Criteria | Test Case ID(s) | Type | Status |
|---|---|---|---|---|
| US-501 | AC-001: Search box + Ctrl+K shortcut | TC-SRCH-001, 002 | E2E Auto | Passed ✓ |
| AC-002: Debounce + auto-suggest | TC-SRCH-003, 004 | E2E + API | Passed ✓ | |
| AC-004: Tiếng Việt không dấu | TC-SRCH-005 | E2E Auto | Failed ✗ BUG-001 | |
| AC-005: Response ≤ 2s (100 users) | TC-PERF-001 | k6 Load Test | Passed ✓ | |
| US-502 | AC-003: Filter state trong URL | TC-FILTER-003 | E2E Auto | Passed ✓ |
| AC-006: Mobile filter → bottom drawer | TC-FILTER-006 | Manual (mobile viewport) | Passed ✓ | |
| US-503 | AC-003: Stock indicator real-time | TC-PROD-003 | Manual + API | Failed ✗ BUG-002 |
| AC-006: Structured data / JSON-LD | TC-SEO-001 | Manual (DevTools) | Passed ✓ | |
| US-504 | AC-004: Guest cart sync on login | TC-CART-004 | E2E Auto | Passed ✓ |
| AC-007: Save for later | TC-CART-007 | E2E Auto | Passed ✓ | |
| US-505 | AC-003: Guest checkout | TC-CHK-003 | E2E Auto | Passed ✓ |
| AC-004: Cascade dropdown address | TC-CHK-004, 005 | E2E + API | Passed ✓ | |
| US-506 | AC-002: 3D Secure flow | TC-PAY-002 | Manual (Stripe test) | Passed ✓ |
| AC-005: Idempotency | TC-PAY-005 | API Auto | Passed ✓ | |
| AC-006: No card data in DB | TC-SEC-008 | DB Query | Passed ✓ | |
| US-507 | AC-004: HMAC-SHA512 verify | TC-PAY-010 | API Auto | Failed ✗ BUG-003 |
| AC-005: Timeout — cart not cleared | TC-PAY-011 | Manual | Passed ✓ | |
| US-508 | AC-005: Minimum order value | TC-COUP-005 | E2E Auto | Passed ✓ |
| Mục | Nội Dung |
|---|---|
| Objectives | Verify Sprint 5 user stories theo AC. Ensure no regression trong Auth, User Profile (Sprint 4). Validate web performance đạt Core Web Vitals (LCP < 2.5s, CLS < 0.1, INP < 200ms). Confirm WCAG 2.1 AA compliance cho checkout flow. |
| In Scope | Product search + filter, Product detail page, Cart management, Checkout (Stripe + VNPay), Coupon, Order confirmation email trigger, Cross-browser testing |
| Out of Scope | Order tracking/shipping updates (Sprint 6), Admin dashboard, Seller portal, Return/refund flow, Native mobile app |
| Test Types | Functional (Playwright E2E + manual), API (Postman/Newman), Database integrity, Cross-browser (Chrome/Safari/Firefox/Edge), Performance (k6 + Lighthouse), Security (OWASP ZAP + manual), Accessibility (axe-core + manual) |
| Browsers | Chrome 120+ ✓, Firefox 121+ ✓, Safari 17+ ✓, Edge 120+ ✓ | Mobile: Chrome Android, Safari iOS |
| Entry Criteria | Build #142 deployed to staging ✓, Swagger docs updated ✓, Dev unit test coverage ≥80% ✓, 5,000 products seeded ✓, VNPay sandbox configured ✓ |
| Exit Criteria | ≥90% test cases executed, Pass rate ≥90%, 0 Critical open, 0 High security vulns, Core Web Vitals pass, WCAG AA critical issues resolved |
| Timeline | Day 1-2: Test design + review | Day 3-7: Functional execution | Day 7-8: Cross-browser + A11y | Day 8-9: Performance + Security | Day 9-10: Bug retest + regression | Day 10: Go/No-Go report |
| Tools | Playwright (E2E), Postman + Newman (API), k6 (performance), Lighthouse CI (Web Vitals), OWASP ZAP (security), axe DevTools (A11y), BrowserStack (cross-browser), Jira (bugs), Allure (reporting) |
| Test Data | 5,000 seeded products (đủ categories), 10 test user accounts (buyer/guest/admin), Stripe test cards (success/fail/3DS), VNPay sandbox credentials, 20 coupon codes (various types) |
Automation bằng Playwright với TypeScript. Page Object Model. Chạy trên Chrome + Firefox + Safari trong CI.
Search & Filter
| TC ID | Test Case | Steps | Expected | Pri | Result |
|---|---|---|---|---|---|
| TC-SRCH-001 | Search happy path | Vào trang chủ → gõ "iPhone 15" vào search box → nhấn Enter | Navigate tới /search?q=iPhone+15. Kết quả hiện ≥1 product có "iPhone 15" trong tên. Breadcrumb: "Kết quả cho 'iPhone 15' (N sản phẩm)" | P1 | PASS |
| TC-SRCH-002 | Keyboard shortcut Ctrl+K | Đang ở bất kỳ trang nào → nhấn Ctrl+K (Mac: Cmd+K) | Search modal/overlay mở ra, focus tự động vào search input | P1 | PASS |
| TC-SRCH-005 | Tìm kiếm không dấu tiếng Việt | Gõ "iphone" (không dấu, chữ thường) vào search | Trả về kết quả có "iPhone" trong tên. Elasticsearch Vietnamese analyzer phải handle được. | P1 | FAIL — BUG-001 |
| TC-SRCH-006 | Auto-suggest dropdown | Gõ "sam" → đợi 300ms | Dropdown hiện max 8 suggestions bắt đầu bằng "Samsung". Click suggestion → navigate search results. | P2 | PASS |
| TC-SRCH-007 | Empty search result | Gõ "xyzabc123notexist" | Empty state: "Không tìm thấy kết quả cho 'xyzabc123notexist'". Hiện 4 sản phẩm gợi ý phổ biến. | P2 | PASS |
| TC-FILTER-001 | Filter theo khoảng giá | Search "laptop" → kéo price range slider: 5 triệu – 20 triệu → kết quả cập nhật | Chỉ hiển thị laptops có giá trong khoảng 5-20 triệu. URL có ?minPrice=5000000&maxPrice=20000000 | P1 | PASS |
| TC-FILTER-003 | Filter state trong URL | Apply filter (brand=Apple, minPrice=10000000) → copy URL → mở tab mới | Tab mới hiển thị đúng filter đã apply — URL params được đọc và applied đúng. | P2 | PASS |
| TC-FILTER-004 | Xóa filter individual | Apply 3 filters → click "×" trên chip "Apple" | Chỉ xóa filter "Apple", 2 filter còn lại vẫn active. Kết quả cập nhật. | P2 | PASS |
Product Detail
| TC ID | Test Case | Steps | Expected | Pri | Result |
|---|---|---|---|---|---|
| TC-PROD-001 | Product page — happy path | Click vào sản phẩm "iPhone 15 Pro 256GB" từ search results | Product detail page load. Có: ảnh gallery (≥1 ảnh), tên, giá (có hoặc không có giá gốc gạch ngang), SKU, mô tả. Breadcrumb đúng. | P1 | PASS |
| TC-PROD-002 | Variant selector — giá thay đổi | Trên iPhone 15 Pro page: chọn variant "512GB" (giá khác "256GB") | Giá update ngay tức thì, ảnh chính chuyển sang ảnh của variant 512GB, SKU thay đổi. | P1 | PASS |
| TC-PROD-003 | Stock indicator — sản phẩm sắp hết | Set stock_quantity = 3 trong DB → reload product page | Hiện "Còn 3 sản phẩm" (warning style). Nếu stock = 0: hiện "Hết hàng", button disabled. | P1 | FAIL — BUG-002 |
| TC-PROD-006 | JSON-LD structured data | DevTools → Elements → tìm <script type="application/ld+json"> | JSON-LD có @type: "Product", name, image, offers.price, offers.availability, aggregateRating. Valid theo schema.org. | P3 | PASS |
Cart & Checkout
| TC ID | Test Case | Steps | Expected | Pri | Result |
|---|---|---|---|---|---|
| TC-CART-001 | Thêm vào giỏ — mini cart slide-in | Click "Thêm vào giỏ hàng" trên product page | Mini cart slide-in từ phải trong <500ms. Hiện tên sản phẩm, ảnh, giá, số lượng, tổng giỏ hàng. Badge icon header cập nhật. | P1 | PASS |
| TC-CART-004 | Guest cart sync khi đăng nhập | Guest: thêm 2 sản phẩm vào cart → đăng nhập → xem cart | Cart sau đăng nhập chứa 2 items của guest. Nếu user đã có cart từ trước: merge (không replace). | P1 | PASS |
| TC-CHK-001 | Checkout happy path — Stripe | Cart có 1 item → Checkout → nhập địa chỉ HCM → chọn GHN Nhanh → dùng Stripe card 4242424242424242 → Place Order | Trang Order Confirmation với order #SVN-XXXXXX, summary đầy đủ. Cart cleared. Email trigger log visible trong server. | P1 | PASS |
| TC-CHK-003 | Guest checkout | Không đăng nhập → cart 1 item → Checkout → nhập email + địa chỉ → thanh toán | Checkout hoàn thành không bắt tạo account. Sau thanh toán: offer tạo account với data đã nhập pre-filled. | P1 | PASS |
| TC-CHK-005 | 3D Secure — Stripe | Dùng Stripe test card 4000002500003155 (3DS required) | 3DS authentication popup/redirect hiện ra. Complete challenge → order confirmed. Decline challenge → "Payment failed". | P1 | PASS |
| TC-CHK-006 | Payment failed — card declined | Dùng Stripe card 4000000000000002 (always declines) | Lỗi "Thẻ bị từ chối. Vui lòng kiểm tra lại thông tin hoặc thử thẻ khác." Ở lại trang checkout. Order KHÔNG được tạo trong DB. | P1 | PASS |
| TC-CHK-008 | VNPay — success flow | Chọn VNPay → redirect tới sandbox → thanh toán thành công → redirect về | URL: /checkout/return?vnp_ResponseCode=00&... Server verify HMAC → order confirmed → order confirmation page. | P1 | PASS |
| TC-COUP-005 | Coupon — minimum order chưa đạt | Coupon "SAVE100K" yêu cầu tối thiểu 500k. Cart = 300k. Nhập coupon. | "Cần mua thêm 200.000₫ để sử dụng mã này." Coupon chưa được apply. | P2 | PASS |
Base URL: https://staging.shopvn.vn/api/v1. Runner: Newman trong GitHub Actions.
GET /products/search
| TC ID | Scenario | Request | Expected Response | Result |
|---|---|---|---|---|
| API-SRCH-001 | Search cơ bản | GET /products/search?q=iPhone&limit=20&offset=0 | 200 OK. Body: {total: N, products: [...], facets: {brands, categories, priceRange}}. Response time < 500ms. |
PASS |
| API-SRCH-002 | Pagination | GET /products/search?q=laptop&limit=10&offset=20 | Trả về sản phẩm từ position 21-30. Có total, hasNextPage, hasPrevPage. |
PASS |
| API-SRCH-003 | XSS injection trong search query | GET /products/search?q=<script>alert(1)</script> | 200 OK với kết quả (empty hoặc có). Response body KHÔNG chứa unescaped <script>. Query được sanitized. |
PASS |
POST /orders + Payment flow
| TC ID | Scenario | Input | Expected | Result |
|---|---|---|---|---|
| API-PAY-005 | Idempotency — double submit | POST /orders 2 lần cùng idempotency key | Cả 2 trả về 201 nhưng CÙNG orderId. Chỉ 1 record trong DB. Chỉ charge 1 lần. | PASS |
| API-PAY-010 | VNPay webhook — HMAC verification | POST /webhooks/vnpay với vnp_SecureHash sai | 200 OK (tránh VNPay retry) nhưng order NOT updated. Log "Invalid HMAC signature". | FAIL — BUG-003 |
| API-PAY-011 | Cart price tamper | POST /orders với body tự set "totalAmount": 1 |
Server recalculate từ DB prices, ignore client amount. Order tạo với giá đúng. | PASS |
| API-COUP-001 | Validate coupon trước checkout | POST /coupons/validate {"code":"SAVE100K","cartTotal":300000} |
422 Unprocessable Entity. {"error":"MINIMUM_NOT_MET","shortfall":200000} |
PASS |
ab -n 50 -c 50 -p order.json -T application/json https://staging.shopvn.vn/api/v1/orders → stock đếm âm. Tương tự BUG-004 trong Mobile Project — cần atomic UPDATE với WHERE stock_quantity > 0. → BUG-004 (Critical).
Platform: BrowserStack cho real devices + Playwright multi-browser CI. Focus vào checkout flow và product display.
| TC ID | Test Case | Chrome | Firefox | Safari | Edge | Safari iOS |
|---|---|---|---|---|---|---|
| TC-XB-001 | Product image gallery — zoom on hover | PASS | PASS | FAIL | PASS | N/A (touch) |
| TC-XB-002 | Checkout form — address cascade dropdown | PASS | PASS | PASS | PASS | PASS |
| TC-XB-003 | Stripe payment form render | PASS | PASS | PASS | PASS | PASS |
| TC-XB-004 | Price range slider (filter) | PASS | PASS | FAIL | PASS | FAIL |
| TC-XB-005 | Mini cart animation slide-in | PASS | PASS | PASS | PASS | PASS |
| TC-XB-006 | Mobile filter — bottom drawer | N/A | N/A | N/A | N/A | PASS |
| TC-XB-007 | Font rendering — tiếng Việt | PASS | PASS | PASS | PASS | PASS |
offsetX/offsetY không tương thích Safari. Fix: dùng getBoundingClientRect().TC-XB-004: Range input slider styling không apply trên Safari (WebKit cần
-webkit-appearance reset). Priority P2 — price filter vẫn hoạt động qua input text fallback.
Hai loại: k6 (backend load test) + Lighthouse CI (Core Web Vitals frontend). SLA: P90 < 2s, Core Web Vitals pass.
Performance Results — Sprint 5
| Endpoint | P50 | P90 | P99 | Error Rate | SLA |
|---|---|---|---|---|---|
| GET /products (browse) | 95ms | 280ms | 590ms | 0.02% | ✓ Pass |
| GET /products/search | 120ms | 390ms | 820ms | 0.05% | ✓ Pass |
| POST /cart/items | 55ms | 140ms | 310ms | 0.01% | ✓ Pass |
| POST /orders (Stripe) | 780ms | 1,890ms | 3,200ms | 0.3% | ✓ Pass (barely) |
| GET /address/provinces | 8ms | 22ms | 45ms | 0% | ✓ Pass (cached) |
Core Web Vitals — Lighthouse CI
| Page | LCP | INP | CLS | FCP | Status |
|---|---|---|---|---|---|
| Home Page | 1.8s ✓ | 145ms ✓ | 0.05 ✓ | 0.9s | ✓ All Pass |
| Product List | 2.1s ✓ | 180ms ✓ | 0.12 ✗ | 1.1s | ✗ CLS = 0.12 (threshold 0.1) — BUG-006 |
| Product Detail | 2.3s ✓ | 95ms ✓ | 0.04 ✓ | 1.2s | ✓ All Pass |
| Checkout | 1.6s ✓ | 110ms ✓ | 0.02 ✓ | 0.8s | ✓ All Pass |
aspect-ratio: 1/1 hoặc width/height attributes trên img tags để browser reserve space. Priority P2.
| TC ID | OWASP | Test Scenario | Expected | Result |
|---|---|---|---|---|
| TC-SEC-001 | A01: Access Control | User A xem order của User B: GET /orders/SVN-000001 với token của User B | 403 Forbidden. Order data không leak. | PASS |
| TC-SEC-002 | A01: IDOR — cart | GET /cart?userId=other-user-id | API ignore userId param, trả về cart của authenticated user. Không thể xem cart người khác. | PASS |
| TC-SEC-003 | A03: XSS | Product review submission với <img src=x onerror=alert(1)> trong text |
Review được sanitized (DOMPurify hoặc server-side). Hiển thị dưới dạng text, không execute script. | PASS |
| TC-SEC-004 | A03: SQL Injection | GET /products/search?q='; DROP TABLE products;-- | 200 với kết quả empty hoặc 400. DB không bị modified. Parameterized queries. | PASS |
| TC-SEC-005 | A04: Insecure Design | POST /orders với tự đặt giá: {"items":[{"price":1}]} |
Server recalculate từ DB. Order total = giá thực tế, không dùng client-submitted price. | PASS |
| TC-SEC-006 | A05: Security Headers | Kiểm tra HTTP response headers | Có: HSTS, X-Frame-Options:DENY, X-Content-Type-Options:nosniff, CSP. Không có: X-Powered-By, Server version. | PASS |
| TC-SEC-007 | A07: Auth | Cookie: Kiểm tra session cookie attributes | Cookies có: HttpOnly, Secure, SameSite=Strict. Không accessible via JavaScript. |
PASS |
| TC-SEC-008 | A02: Cryptographic | SELECT * FROM payments — có card data không? | Chỉ có stripe_payment_method_id (pm_xxx). Không có card_number, cvv, expiry. |
PASS |
| TC-SEC-009 | A10: SSRF | Product import URL field: nhập http://169.254.169.254/latest/meta-data/ (AWS metadata) |
Request bị blocked. Không trả về AWS internal metadata. URL allowlist hoặc block internal ranges. | PASS |
| TC-SEC-010 | A09: Logging | Trigger failed login 10 lần → check logs | Logs có: timestamp, IP, user email (partial mask). Không có: password, full card number trong bất kỳ log nào. | PASS |
Tools: axe DevTools (automated) + manual keyboard + NVDA (Windows) / VoiceOver (Mac). Focus: Checkout flow (highest barrier for users with disabilities).
| TC ID | Test Case | Method | Expected (WCAG) | Result |
|---|---|---|---|---|
| TC-A11Y-001 | Keyboard navigation — checkout flow | Manual: Tab through toàn bộ checkout | Mọi interactive element đạt được bằng Tab. Focus indicator visible (outline). Không bị "focus trap" ngoài modal. (WCAG 2.1.1) | PASS |
| TC-A11Y-002 | Form labels — checkout inputs | axe DevTools + manual | Mọi input có label liên kết (for/id hoặc aria-label). Error messages có aria-describedby. (WCAG 1.3.1) | PASS |
| TC-A11Y-003 | Color contrast — button và text | axe DevTools | Contrast ratio: normal text ≥4.5:1, large text ≥3:1. (WCAG 1.4.3) | FAIL — BUG-007 |
| TC-A11Y-004 | Screen reader — product list | NVDA + Chrome | Mỗi product card đọc: "Tên sản phẩm, giá, số sao rating, Nút Thêm vào giỏ". Không đọc thừa decorative images. (WCAG 1.1.1) | PASS |
| TC-A11Y-005 | Error messages — form validation | axe + VoiceOver | Khi field invalid: error message được announce ngay (aria-live="polite" hoặc focus shift). (WCAG 3.3.1) | PASS |
| TC-A11Y-006 | Skip navigation link | Tab từ đầu trang | First Tab press hiện "Skip to main content" link. (WCAG 2.4.1) | PASS |
| TC-A11Y-007 | Modal — Stripe payment form | Keyboard + Screen reader | Khi payment modal mở: focus chuyển vào modal, Esc đóng, focus trap trong modal, focus trở về trigger khi đóng. (WCAG 2.1.2) | PASS |
7 bugs tìm được. 1 Critical, 3 Major, 2 Minor, 1 Trivial.
icu_folding token filter. Fix: thêm ICU Analysis plugin, rebuild index với lowercase + ascii_folding filters.stock < 5 thay vì stock <= 5 — off-by-one.{"vnp_ResponseCode":"00","vnp_TxnRef":"SVN-000001","vnp_SecureHash":"fakehash"}. 2. Send request. 3. Check order status trong DB.computedHash = HMAC_SHA512(secret, sortedParams); if (computedHash !== vnp_SecureHash) throw 'Invalid signature'UPDATE product_variants SET stock_quantity = stock_quantity - $qty WHERE id = $id AND stock_quantity >= $qty RETURNING id. Kiểm tra rowCount = 1, nếu không → throw 409 Out of Stock.event.offsetX/offsetY — deprecated/inconsistent trên Safari. Fix: const rect = img.getBoundingClientRect(); x = event.clientX - rect.left;| KPI | Target | Actual | Status |
|---|---|---|---|
| Test Execution Rate | ≥90% | 92.7% (51/55) | ✓ Met |
| Pass Rate | ≥90% | 94.1% (48/51) | ✓ Met |
| Critical Bugs Open | 0 | 2 (BUG-003 VNPay, BUG-004 race condition) | ✗ NOT Met |
| Core Web Vitals | All Pass | CLS fail on Product List (BUG-006) | ✗ NOT Met |
| WCAG 2.1 AA Critical | 0 Critical violations | 1 contrast issue (BUG-007) | ✗ NOT Met |
| API Performance P90 | <2s tất cả | All endpoints <2s ✓ | ✓ Met |
| Cross-browser — Critical paths | Pass Chrome/Firefox/Safari/Edge | 2 Safari issues (non-blocking checkout) | ✓ Conditional |
| Security — Critical vulns | 0 | 1 Critical (BUG-003 VNPay HMAC) | ✗ NOT Met |
| Automation Coverage (P1 TCs) | ≥65% | 76% (19/25 P1 cases automated) | ✓ Met |
| Defect Leakage từ Sprint 4 | <3% | 0% (không tìm được Sprint 4 bugs) | ✓ Met |
Bug Severity Distribution
| Severity | Found | Fixed | Open | Deferred |
|---|---|---|---|---|
| Critical | 2 | 0 | 2 | 0 |
| Major | 3 | 1 | 1 | 1 |
| Minor | 2 | 1 | 0 | 1 |
| Total | 7 | 2 | 3 | 2 |
| Criteria | Requirement | Actual | Decision |
|---|---|---|---|
| Test Pass Rate | ≥90% | 94.1% | ✅ Go |
| Critical Bugs | 0 open | 2 open (VNPay HMAC, Race condition) | 🔴 No-Go |
| Security Vulnerabilities | 0 Critical | 1 Critical (BUG-003) | 🔴 No-Go |
| Core Web Vitals | All Pass | CLS = 0.12 (fail) on Product List | 🟡 Conditional |
| WCAG 2.1 AA | 0 critical A11y issues | 1 contrast issue (BUG-007, P3 impact) | 🟡 Conditional |
| API Performance | P90 <2s | All endpoints <2s ✓ | ✅ Go |
| Cross-browser Checkout | Pass tất cả browsers | Checkout works on all browsers. Safari image zoom fail (non-blocking) | ✅ Go |
| Regression | 0 regressions Sprint 4 | 0 regressions | ✅ Go |
| Vietnamese Search | Support tiếng Việt không dấu | BUG-001 — search không dấu fail | 🔴 No-Go |
Recommendation: NO-GO
Blockers bắt buộc fix trước release:
- BUG-003 — VNPay HMAC verification: Security critical. Attacker có thể confirm orders miễn phí bằng forged webhook. Estimated fix: 4 giờ.
- BUG-004 — Race condition inventory: Có thể oversell trong flash sale hoặc khi nhiều user cùng mua sản phẩm ít hàng. Estimated fix: 6 giờ.
- BUG-001 — Vietnamese search: Cốt lõi của product discovery cho user VN. 70%+ users gõ không dấu. Estimated fix: 1 ngày (rebuild ES index).
Có thể release sau khi fix 3 blockers trên (với risk acceptance từ PM cho các mục sau):
- CLS 0.12 trên Product List → impact SEO nhưng không ảnh hưởng user flow. Monitor Core Web Vitals sau deploy.
- Safari image zoom → ảnh hưởng ~18% users, feature non-critical. Fix Sprint 6.
- WCAG contrast "Hết hàng" → fix cùng sprint 6 với accessibility audit toàn trang.
Revised release estimate: +2 ngày (fix 3 blockers + 1 ngày regression test mini).
Signed: QA Engineer — Sprint 5 | ShopVN v2.1 | Date: 2026-06-04
2. Vietnamese search cần spike sớm: Elasticsearch Vietnamese analyzer config là technical prerequisite — nên được verify trong Sprint 1 (foundation sprint) thay vì phát hiện lúc QA.
3. CLS phải test trong CI, không chờ QA: Lighthouse CI nên chạy trong GitHub Actions trên mỗi PR, không phải chỉ khi QA chạy manual. Developer biết CLS fail ngay khi code.
4. Race condition là pattern lặp lại: Mọi inventory decrement, coupon use, flash sale phải có explicit review về concurrency trong Design Review. Add vào PR checklist.