# 🐛 BUG REPORT TEMPLATE — API & UI TESTING

> **Phiên bản template:** v1.2 | **Cập nhật lần cuối:** 2024 | **Ngôn ngữ:** Tiếng Việt

---

## 📋 MỤC LỤC

1. [Template Gốc](#-template-gốc)
2. [Ví dụ 1 — Bug API](#-ví-dụ-1--bug-api)
3. [Ví dụ 2 — Bug UI](#-ví-dụ-2--bug-ui)
4. [Bảng Quy Ước Severity & Priority](#-bảng-quy-ước-severity--priority)
5. [Checklist Trước Khi Submit](#-checklist-trước-khi-submit)

---

## 📄 TEMPLATE GỐC

---

### 🔖 THÔNG TIN CƠ BẢN

| Trường | Nội dung |
|---|---|
| **Bug ID** | `BUG-[PROJECT]-[NUMBER]` *(VD: BUG-SHOP-0042)* |
| **Tiêu đề** | *(Mô tả ngắn gọn, rõ ràng trong 1 câu — Ai / Làm gì / Ở đâu / Kết quả sai)* |
| **Loại bug** | ☐ API ☐ UI ☐ API + UI ☐ Logic/Business |
| **Người báo cáo** | *(Họ tên — Email)* |
| **Ngày tạo** | `YYYY-MM-DD` |
| **Ngày cập nhật** | `YYYY-MM-DD` |
| **Trạng thái** | ☐ New ☐ In Progress ☐ Fixed ☐ Verified ☐ Closed ☐ Reopened |
| **Gán cho (Dev)** | *(Tên dev phụ trách)* |

---

### 🌐 MÔI TRƯỜNG KIỂM THỬ

| Trường | Nội dung |
|---|---|
| **Môi trường** | ☐ Development ☐ Staging ☐ UAT ☐ Production |
| **Base URL** | *(VD: `https://staging-api.example.com`)* |
| **Trình duyệt** | *(VD: Chrome 124.0.6367 / Firefox 125 / Safari 17)* |
| **Hệ điều hành** | *(VD: Windows 11 / macOS Sonoma 14.4 / Ubuntu 22.04)* |
| **Thiết bị** | *(VD: Desktop / iPhone 14 Pro / Samsung Galaxy S23)* |
| **Độ phân giải** | *(VD: 1920×1080 / 375×812)* |
| **Phiên bản App** | *(VD: v2.3.1 — Build #4521)* |
| **Tool kiểm thử** | *(VD: Postman v10.22 / Insomnia / Bruno / curl)* |
| **Token/Auth** | *(Ghi rõ loại auth đang dùng — KHÔNG paste token thật)* |

---

### 🚨 MỨC ĐỘ ƯU TIÊN

| Trường | Nội dung |
|---|---|
| **Severity** | ☐ 🔴 Critical ☐ 🟠 High ☐ 🟡 Medium ☐ 🟢 Low |
| **Priority** | ☐ 🔴 P1 — Urgent ☐ 🟠 P2 — High ☐ 🟡 P3 — Medium ☐ 🟢 P4 — Low |
| **Tần suất xuất hiện** | ☐ Luôn luôn (100%) ☐ Thường xuyên (>50%) ☐ Thỉnh thoảng (<50%) ☐ Hiếm khi |
| **Ảnh hưởng** | *(Mô tả ngắn: tính năng nào bị ảnh hưởng, bao nhiêu % user bị tác động)* |

---

### 📝 MÔ TẢ LỖI

```
[Mô tả tổng quan về lỗi]

Lỗi xảy ra khi: ...
Điều kiện tiên quyết: ...
Dữ liệu test đã dùng: ...
Lỗi có thể liên quan đến: ...
```

---

### 🔌 THÔNG TIN API *(Bỏ qua nếu chỉ là bug UI thuần túy)*

#### Request

```
Method  : [GET / POST / PUT / PATCH / DELETE]
Endpoint: [Full URL + Query Params nếu có]
```

**Headers:**
```http
Content-Type : application/json
Authorization: Bearer <token_type_not_real_value>
X-Request-ID : [nếu có]
[Thêm header khác nếu cần]
```

**Request Body:**
```json
{
  "field1": "value1",
  "field2": "value2"
}
```
> 💡 *Nếu là GET request thì ghi Query Params thay cho Body*

#### Response Nhận Được *(Actual)*

**Status Code:** `[VD: 500 Internal Server Error]`

**Response Body:**
```json
{
  "error": "...",
  "message": "..."
}
```

**Response Time:** `[VD: 12,340ms — vượt timeout]`

#### Response Mong Đợi *(Expected)*

**Status Code:** `[VD: 200 OK]`

**Response Body:**
```json
{
  "success": true,
  "data": { ... }
}
```

---

### 🖥️ BƯỚC TÁI HIỆN TRÊN UI *(Step by Step)*

> ⚠️ *Mô tả chi tiết từng bước, đủ để người khác tái hiện 100%*

**Điều kiện tiên quyết (Precondition):**
- [ ] Đã có tài khoản test: `[email/username]`
- [ ] Đã đăng nhập vào hệ thống
- [ ] Môi trường: `[Staging/UAT]`
- [ ] Dữ liệu test tồn tại: `[mô tả dữ liệu]`

**Các bước thực hiện:**

| Bước | Hành động | Dữ liệu nhập (nếu có) |
|:---:|---|---|
| 1 | Truy cập URL: `[URL]` | — |
| 2 | Click vào `[tên button/link/element]` | — |
| 3 | Nhập thông tin vào field `[tên field]` | `[giá trị nhập]` |
| 4 | Click `[Submit / Confirm / ...]` | — |
| 5 | Quan sát kết quả hiển thị | — |

---

### ✅ KẾT QUẢ MONG ĐỢI *(Expected Result)*

```
[Mô tả chi tiết điều hệ thống PHẢI làm đúng]

VD:
- API trả về status 200 với body chứa thông tin user
- UI hiển thị thông báo "Thành công" màu xanh
- Dữ liệu được lưu vào database
- Trang chuyển hướng đến /dashboard
```

---

### ❌ KẾT QUẢ THỰC TẾ *(Actual Result)*

```
[Mô tả chi tiết điều hệ thống ĐANG làm sai]

VD:
- API trả về status 500 với message "Internal Server Error"
- UI hiển thị màn hình trắng, không có thông báo lỗi
- Console log ghi: "TypeError: Cannot read property 'id' of undefined"
- Dữ liệu KHÔNG được lưu vào database
```

---

### 📸 SCREENSHOT / EVIDENCE

| STT | Mô tả | File/Link |
|:---:|---|---|
| 1 | Screenshot lỗi trên UI | `[Đính kèm / Link ảnh]` |
| 2 | Postman request & response | `[Đính kèm / Link ảnh]` |
| 3 | Console log / Network tab | `[Đính kèm / Link ảnh]` |
| 4 | Video tái hiện lỗi | `[Link video]` |
| 5 | Log file server | `[Đính kèm / Link]` |

> 📌 *Quy tắc đặt tên file: `BUG-[ID]_[mô tả ngắn]_[YYYYMMDD].[ext]`*
> *VD: `BUG-SHOP-0042_api-500-error_20240315.png`*

---

### 💬 GHI CHÚ CHO DEV

```
[Thông tin bổ sung giúp dev debug nhanh hơn]

🔍 Nghi vấn nguyên nhân:
- ...

📍 Vị trí code có thể liên quan:
- File: [src/controllers/...]
- Function: [...]
- Line: [...]

🔗 Ticket liên quan:
- BUG-XXX: [Mô tả]
- TASK-XXX: [Mô tả]

📊 Log liên quan (Server/Client):
[Paste log nếu có]

⚡ Gợi ý fix (nếu tester biết):
- ...

📅 Deadline fix mong muốn: [YYYY-MM-DD]
```

---
---

## 🔴 VÍ DỤ 1 — BUG API

---

### 🔖 THÔNG TIN CƠ BẢN

| Trường | Nội dung |
|---|---|
| **Bug ID** | `BUG-SHOP-0042` |
| **Tiêu đề** | **[API] POST /api/v1/orders — Server trả về 500 khi tạo đơn hàng với sản phẩm có discount = 100%** |
| **Loại bug** | ☑ API |
| **Người báo cáo** | Nguyễn Thị Lan — lan.nguyen@company.com |
| **Ngày tạo** | `2024-03-15` |
| **Ngày cập nhật** | `2024-03-15` |
| **Trạng thái** | ☑ New |
| **Gán cho (Dev)** | Trần Văn Hùng |

---

### 🌐 MÔI TRƯỜNG KIỂM THỬ

| Trường | Nội dung |
|---|---|
| **Môi trường** | ☑ Staging |
| **Base URL** | `https://staging-api.shopvn.com` |
| **Trình duyệt** | Không áp dụng *(bug thuần API)* |
| **Hệ điều hành** | Windows 11 Pro 23H2 |
| **Thiết bị** | Desktop |
| **Phiên bản App** | v2.3.1 — Build #4521 |
| **Tool kiểm thử** | Postman v10.22.6 |
| **Token/Auth** | Bearer Token — JWT (Role: Customer, User ID: test_user_99) |

---

### 🚨 MỨC ĐỘ ƯU TIÊN

| Trường | Nội dung |
|---|---|
| **Severity** | ☑ 🔴 Critical |
| **Priority** | ☑ 🔴 P1 — Urgent |
| **Tần suất xuất hiện** | ☑ Luôn luôn (100%) |
| **Ảnh hưởng** | Toàn bộ flow tạo đơn hàng bị block khi áp mã giảm giá 100%. Ảnh hưởng trực tiếp đến chiến dịch khuyến mãi Flash Sale ngày 20/03/2024. Ước tính ~15% đơn hàng trong campaign sẽ bị lỗi. |

---

### 📝 MÔ TẢ LỖI

```
API tạo đơn hàng (POST /api/v1/orders) trả về HTTP 500 Internal Server Error
thay vì tạo đơn hàng thành công khi sản phẩm trong giỏ hàng có mã giảm giá
áp dụng discount = 100% (sản phẩm miễn phí hoàn toàn).

Lỗi xảy ra khi:
  - Sản phẩm có coupon_code với discount_type = "percentage" và discount_value = 100
  - Final price sau khi tính = 0 VNĐ
  - Hệ thống có thể đang thực hiện phép chia cho 0 khi tính phí

Điều kiện tiên quyết:
  - Tồn tại coupon "FREESHIP100" với discount 100% còn hiệu lực
  - Sản phẩm áp dụng coupon này có giá gốc > 0

Dữ liệu test đã dùng:
  - Product ID: prod_8821 (Áo thun nam — 150,000 VNĐ)
  - Coupon: FREESHIP100 (100% off, expires: 2024-03-31)
  - User: test_customer_99@gmail.com

Lỗi có thể liên quan đến:
  - Logic tính toán final_price khi = 0 (division by zero hoặc null check thiếu)
  - Validation của payment_amount trước khi ghi database
```

---

### 🔌 THÔNG TIN API

#### Request

```
Method  : POST
Endpoint: https://staging-api.shopvn.com/api/v1/orders
```

**Headers:**
```http
Content-Type  : application/json
Authorization : Bearer <jwt_customer_token>
X-Request-ID  : req_20240315_0042
Accept        : application/json
```

**Request Body:**
```json
{
  "items": [
    {
      "product_id": "prod_8821",
      "product_name": "Áo thun nam basic",
      "quantity": 1,
      "unit_price": 150000,
      "currency": "VND"
    }
  ],
  "shipping_address": {
    "full_name": "Nguyễn Văn Test",
    "phone": "0912345678",
    "address": "123 Đường Lê Lợi",
    "ward": "Phường Bến Nghé",
    "district": "Quận 1",
    "province": "Hồ Chí Minh"
  },
  "coupon_code": "FREESHIP100",
  "payment_method": "cod",
  "note": "Giao giờ hành chính"
}
```

---

#### Response Nhận Được *(Actual)*

**Status Code:** `500 Internal Server Error`

**Response Time:** `8,234ms` *(bất thường — thường < 500ms)*

**Response Body:**
```json
{
  "status": "error",
  "code": 500,
  "message": "Internal Server Error",
  "request_id": "req_20240315_0042",
  "timestamp": "2024-03-15T09:23:41.123Z"
}
```

> 🔍 **Server Log kèm theo** *(lấy từ team DevOps)*:
```
ERROR [2024-03-15 09:23:41] OrderController.createOrder
ArithmeticException: Division by zero
  at calculateTaxAmount (services/pricing.service.js:87)
  at applyDiscount (services/pricing.service.js:134)
  at OrderService.processPayment (services/order.service.js:223)
  at OrderController.createOrder (controllers/order.controller.js:45)
```

---

#### Response Mong Đợi *(Expected)*

**Status Code:** `201 Created`

**Response Body:**
```json
{
  "status": "success",
  "code": 201,
  "message": "Đơn hàng đã được tạo thành công",
  "data": {
    "order_id": "ORD-20240315-XXXXX",
    "order_status": "pending",
    "items": [
      {
        "product_id": "prod_8821",
        "product_name": "Áo thun nam basic",
        "quantity": 1,
        "unit_price": 150000,
        "discount_amount": 150000,
        "final_price": 0
      }
    ],
    "pricing_summary": {
      "subtotal": 150000,
      "discount": 150000,
      "coupon_code": "FREESHIP100",
      "shipping_fee": 0,
      "tax": 0,
      "total_amount": 0
    },
    "payment_method": "cod",
    "estimated_delivery": "2024-03-17",
    "created_at": "2024-03-15T09:23:41.000Z"
  }
}
```

---

### 🖥️ BƯỚC TÁI HIỆN TRÊN UI

> *Bug này chủ yếu là API, nhưng có thể tái hiện qua UI như sau:*

**Điều kiện tiên quyết:**
- [x] Tài khoản test: `test_customer_99@gmail.com` / `Test@123`
- [x] Đã đăng nhập vào `https://staging.shopvn.com`
- [x] Coupon `FREESHIP100` đang active trong hệ thống
- [x] Sản phẩm `prod_8821` còn hàng (stock > 0)

| Bước | Hành động | Dữ liệu nhập (nếu có) |
|:---:|---|---|
| 1 | Truy cập `https://staging.shopvn.com` | — |
| 2 | Đăng nhập bằng tài khoản test | `test_customer_99@gmail.com` / `Test@123` |
| 3 | Tìm kiếm và chọn sản phẩm "Áo thun nam basic" (ID: prod_8821) | — |
| 4 | Click **"Thêm vào giỏ hàng"** | — |
| 5 | Click icon giỏ hàng → Click **"Tiến hành thanh toán"** | — |
| 6 | Điền đầy đủ địa chỉ giao hàng | *(Xem dữ liệu trong Request Body)* |
| 7 | Nhập mã giảm giá vào ô "Mã khuyến mãi" → Click **"Áp dụng"** | `FREESHIP100` |
| 8 | Xác nhận giá sau giảm hiển thị là **0 VNĐ** | — |
| 9 | Chọn phương thức thanh toán **"Thanh toán khi nhận hàng (COD)"** | — |
| 10 | Click **"Đặt hàng"** | — |
| 11 | Quan sát kết quả — trang loading kéo dài ~8 giây rồi hiện thông báo lỗi đỏ | — |

---

### ✅ KẾT QUẢ MONG ĐỢI

```
✔ API trả về HTTP 201 Created
✔ Hệ thống tạo thành công đơn hàng với total_amount = 0 VNĐ
✔ UI hiển thị trang "Đặt hàng thành công" với mã đơn hàng
✔ Email xác nhận đơn hàng gửi đến test_customer_99@gmail.com
✔ Đơn hàng xuất hiện trong trang "Quản lý đơn hàng" của admin
✔ Kho hàng trừ đúng số lượng (stock giảm 1)
✔ Coupon FREESHIP100 trừ đúng lượt sử dụng
```

---

### ❌ KẾT QUẢ THỰC TẾ

```
✘ API trả về HTTP 500 Internal Server Error sau ~8 giây
✘ Server log ghi nhận: "ArithmeticException: Division by zero" 
  tại pricing.service.js:87 (hàm calculateTaxAmount)
✘ UI hiển thị thông báo lỗi đỏ: "Đặt hàng thất bại. Vui lòng thử lại."
  (không có mã đơn hàng, không có thông tin lỗi cụ thể cho user)
✘ Đơn hàng KHÔNG được tạo trong database
✘ Email xác nhận KHÔNG được gửi
✘ Kho hàng KHÔNG thay đổi
✘ Coupon KHÔNG bị trừ lượt sử dụng
```

---

### 📸 SCREENSHOT / EVIDENCE

| STT | Mô tả | File/Link |
|:---:|---|---|
| 1 | Postman — Request gửi đi với body đầy đủ | `BUG-SHOP-0042_postman-request_20240315.png` |
| 2 | Postman — Response 500 nhận về | `BUG-SHOP-0042_postman-response-500_20240315.png` |
| 3 | UI — Màn hình checkout với coupon FREESHIP100 đã áp dụng (giá 0đ) | `BUG-SHOP-0042_ui-checkout-0vnd_20240315.png` |
| 4 | UI — Thông báo lỗi sau khi click "Đặt hàng" | `BUG-SHOP-0042_ui-error-msg_20240315.png` |
| 5 | Server log — Stack trace đầy đủ | `BUG-SHOP-0042_server-log_20240315.txt` |
| 6 | Video tái hiện toàn bộ flow | `BUG-SHOP-0042_reproduce-video_20240315.mp4` |
| 7 | Postman Collection export | `BUG-SHOP-0042_postman-collection.json` |

---

### 💬 GHI CHÚ CHO DEV

```
🔍 Nghi vấn nguyên nhân:
  - Hàm calculateTaxAmount() tại pricing.service.js:87 đang tính thuế
    bằng cách chia cho final_price. Khi final_price = 0 (do discount 100%)
    → xảy ra Division by Zero
  - Thiếu guard clause: if (finalPrice === 0) return 0; trước phép tính

📍 Vị trí code có thể liên quan:
  - File: src/services/pricing.service.js
  - Function: calculateTaxAmount() — Line ~87
  - Function: applyDiscount() — Line ~134
  - File: src/services/order.service.js
  - Function: processPayment() — Line ~223

🔗 Ticket liên quan:
  - TASK-1205: Tạo tính năng coupon discount (đã done — có thể thiếu edge case)
  - BUG-SHOP-0038: Lỗi tương tự với shipping_fee = 0 (đã fix tuần trước)

📊 Log liên quan (Server):
  ERROR [2024-03-15 09:23:41] ArithmeticException: Division by zero
  at calculateTaxAmount (services/pricing.service.js:87)
  at applyDiscount (services/pricing.service.js:134)
  at OrderService.processPayment (services/order.service.js:223)

⚡ Gợi ý fix:
  1. Thêm null/zero check trước khi thực hiện phép chia:
     if (!finalPrice || finalPrice === 0) return { tax: 0, total: 0 };
  2. Review toàn bộ các phép tính trong pricing.service.js với giá = 0
  3. Thêm unit test cho edge case: discount = 100%, price = 0

📅 Deadline fix mong muốn: 2024-03-18
  (trước Flash Sale Campaign ngày 20/03/2024)

⚠️ NOTE THÊM:
  - Đã test với discount = 50%, 70%, 99% → KHÔNG bị lỗi
  - Chỉ xảy ra khi discount = 100% (final = 0)
  - Cần kiểm tra thêm edge case: discount > 100% (nếu có thể nhập)
```

---
---

## 🟠 VÍ DỤ 2 — BUG UI

---

### 🔖 THÔNG TIN CƠ BẢN

| Trường | Nội dung |
|---|---|
| **Bug ID** | `BUG-SHOP-0057` |
| **Tiêu đề** | **[UI] Trang "Lịch sử đơn hàng" — Nút "Hủy đơn" hiển thị sai với đơn hàng status "Đang giao" trên mobile Safari** |
| **Loại bug** | ☑ UI |
| **Người báo cáo** | Phạm Minh Khoa — khoa.pham@company.com |
| **Ngày tạo** | `2024-03-16` |
| **Ngày cập nhật** | `2024-03-16` |
| **Trạng thái** | ☑ New |
| **Gán cho (Dev)** | Lê Thị Hoa (Frontend) |

---

### 🌐 MÔI TRƯỜNG KIỂM THỬ

| Trường | Nội dung |
|---|---|
| **Môi trường** | ☑ Staging |
| **Base URL** | `https://staging.shopvn.com` |
| **Trình duyệt** | Safari 17.3.1 *(Lỗi)* — Chrome 124 Mobile *(Không lỗi)* |
| **Hệ điều hành** | iOS 17.3.1 |
| **Thiết bị** | iPhone 14 Pro (Real Device) + iPhone 13 (Simulator Xcode 15) |
| **Độ phân giải** | 390×844 (iPhone 14 Pro) / 375×667 (iPhone 13) |
| **Phiên bản App** | Web App v2.3.1 — Build #4521 |
| **Tool kiểm thử** | Safari DevTools (Remote Debug qua Mac) + BrowserStack |
| **Token/Auth** | Session Cookie (đăng nhập thường) |

---

### 🚨 MỨC ĐỘ ƯU TIÊN

| Trường | Nội dung |
|---|---|
| **Severity** | ☑ 🟠 High |
| **Priority** | ☑ 🟠 P2 — High |
| **Tần suất xuất hiện** | ☑ Luôn luôn (100%) — chỉ trên Safari iOS |
| **Ảnh hưởng** | Nút "Hủy đơn" hiển thị với đơn hàng "Đang giao" — user CÓ THỂ hủy sai đơn hàng đã ra shipper. Ảnh hưởng ~23% user dùng Safari iOS theo Analytics. |

---

### 📝 MÔ TẢ LỖI

```
Trên trình duyệt Safari iOS (iPhone), trang "Lịch sử đơn hàng" hiển thị
nút "Hủy đơn" (màu đỏ) với CẢ những đơn hàng có status = "Đang giao"
(shipping). Theo business rule, chỉ được phép hủy đơn khi status là
"Chờ xác nhận" hoặc "Đã xác nhận".

Điều này gây nguy hiểm vì:
  1. User có thể nhầm và click "Hủy đơn" với đơn đang giao
  2. Tuy nhiên, khi click — API từ chối (trả 400) nhưng UI không
     thông báo lỗi rõ ràng, gây confuse cho user

Vấn đề KHÔNG xảy ra trên:
  - Chrome (Desktop & Mobile)
  - Firefox (Desktop)
  - Samsung Internet

Vấn đề XẢY RA trên:
  - Safari 17 / iOS 17 (iPhone 14 Pro — real device)
  - Safari 16 / iOS 16 (iPhone 13 — BrowserStack)
  - Safari 15 / iOS 15 (BrowserStack)

Điều kiện tiên quyết:
  - Tài khoản có ít nhất 1 đơn hàng với status "Đang giao"
  - Truy cập bằng Safari trên iOS

Nghi vấn kỹ thuật:
  - CSS/JS conditional rendering dùng feature không được Safari hỗ trợ
  - Có thể liên quan đến cách Safari xử lý Optional Chaining (?.)
    hoặc Nullish Coalescing (??) trong JS
```

---

### 🔌 THÔNG TIN API *(Liên quan gián tiếp — để dev hiểu context)*

> *Bug chính là UI, nhưng ghi lại API để dev hiểu flow*

#### API Lấy Danh Sách Đơn Hàng

```
Method  : GET
Endpoint: https://staging-api.shopvn.com/api/v1/orders?page=1&limit=10
```

**Headers:**
```http
Authorization: Bearer <jwt_customer_token>
Accept       : application/json
```

**Response (Đúng — API hoạt động bình thường):**
```json
{
  "status": "success",
  "data": {
    "orders": [
      {
        "order_id": "ORD-20240310-00891",
        "status": "shipping",
        "status_label": "Đang giao",
        "can_cancel": false,
        "items": [...],
        "total_amount": 350000,
        "created_at": "2024-03-10T14:30:00Z"
      },
      {
        "order_id": "ORD-20240314-00923",
        "status": "pending",
        "status_label": "Chờ xác nhận",
        "can_cancel": true,
        "items": [...],
        "total_amount": 200000,
        "created_at": "2024-03-14T09:15:00Z"
      }
    ],
    "pagination": {
      "total": 2,
      "page": 1,
      "limit": 10
    }
  }
}
```

> ⚠️ **Quan trọng:** API trả về `"can_cancel": false` đúng với đơn "Đang giao".
> Lỗi nằm ở **UI không đọc đúng field `can_cancel`** trên Safari,
> vẫn render nút "Hủy đơn" bất kể giá trị trả về.

---

### 🖥️ BƯỚC TÁI HIỆN TRÊN UI *(Step by Step)*

**Điều kiện tiên quyết:**
- [x] Tài khoản test: `safari_test_user@gmail.com` / `Test@123`
- [x] Tài khoản có đơn hàng `ORD-20240310-00891` với status **"Đang giao"**
- [x] Thiết bị: iPhone (thật hoặc simulator) với Safari
- [x] Đã kết nối Safari remote debug nếu cần xem console

| Bước | Hành động | Dữ liệu nhập (nếu có) |
|:---:|---|---|
| 1 | Mở Safari trên iPhone → Truy cập `https://staging.shopvn.com` | — |
| 2 | Click **"Đăng nhập"** | — |
| 3 | Nhập email và mật khẩu → Click **"Đăng nhập"** | `safari_test_user@gmail.com` / `Test@123` |
| 4 | Sau khi đăng nhập → Click vào **Avatar / Tên tài khoản** ở header | — |
| 5 | Chọn **"Lịch sử đơn hàng"** từ dropdown menu | — |
| 6 | Quan sát danh sách đơn hàng — tìm đơn **ORD-20240310-00891** | — |
| 7 | Kiểm tra status của đơn: nhãn hiển thị **"Đang giao"** (màu cam) | — |
| 8 | **Quan sát xem có nút "Hủy đơn" không** ← *Đây là điểm kiểm tra* | — |
| 9 | *(Tùy chọn)* Click nút "Hủy đơn" nếu có → Quan sát phản hồi | — |
| 10 | So sánh: Mở cùng URL trên Chrome mobile → nút không xuất hiện | — |

**Bước kiểm tra nâng cao *(cho dev)*:**

| Bước | Hành động |
|:---:|---|
| A | Bật Safari Remote Debug (Mac: Safari → Develop → [iPhone name]) |
| B | Mở Console tab → Quan sát lỗi JS khi trang load |
| C | Vào Elements tab → Inspect nút "Hủy đơn" → Xem CSS display property |
| D | Vào Network tab → Kiểm tra response của GET /api/v1/orders |

---

### ✅ KẾT QUẢ MONG ĐỢI

```
✔ Đơn hàng "ORD-20240310-00891" có status "Đang giao" (shipping)
✔ Nút "Hủy đơn" KHÔNG xuất hiện với đơn có status "Đang giao"
✔ Chỉ hiển thị các nút: "Xem chi tiết" và "Đánh giá sản phẩm" (nếu đã giao)
✔ Behavior nhất quán trên mọi trình duyệt: Chrome, Firefox, Safari
✔ Field can_cancel = false từ API được UI đọc đúng và ẩn nút hủy
✔ Layout đơn hàng hiển thị đúng trên màn hình 390px (iPhone 14 Pro)
```

**Mockup trạng thái đúng:**
```
┌─────────────────────────────────────┐
│ ORD-20240310-00891                  │
│ Áo thun nam basic × 1              │
│ 150,000 VNĐ          [Đang giao 🚚] │
│                                     │
│         [📦 Xem chi tiết]           │
│    ❌ KHÔNG có nút "Hủy đơn"        │
└─────────────────────────────────────┘
```

---

### ❌ KẾT QUẢ THỰC TẾ

```
✘ Nút "Hủy đơn" (màu đỏ) hiển thị với đơn hàng có status "Đang giao"
✘ UI bỏ qua / đọc sai field can_cancel từ API response
✘ Khi user click "Hủy đơn" → Modal xác nhận hiện ra → Click "Xác nhận hủy"
  → API trả về 400 Bad Request nhưng UI chỉ quay spinner rồi... 
  không làm gì (không thông báo lỗi, không đóng modal)
✘ User bị kẹt ở modal "Đang hủy đơn..." không thoát được
  (phải reload trang)
✘ Giao diện trên Safari nhỏ hơn bình thường — các nút action bị đè lên nhau
✘ Console Safari ghi: "TypeError: order.can_cancel?.toString is not a function"
  (optional chaining ?. không hoạt động đúng trên Safari 15)
```

**Màn hình thực tế bị lỗi:**
```
┌─────────────────────────────────────┐
│ ORD-20240310-00891                  │
│ Áo thun nam basic × 1              │
│ 150,000 VNĐ          [Đang giao 🚚] │
│                                     │
│  [📦 Xem chi tiết] [❌ Hủy đơn]   │
│  ← NÚT HỦY ĐƠN KHÔNG NÊN CÓ Ở ĐÂY │
└─────────────────────────────────────┘
```

---

### 📸 SCREENSHOT / EVIDENCE

| STT | Mô tả | File/Link |
|:---:|---|---|
| 1 | Safari iOS — Nút "Hủy đơn" hiển thị sai với đơn "Đang giao" | `BUG-SHOP-0057_safari-cancel-btn-wrong_20240316.png` |
| 2 | Chrome iOS — Đúng: không có nút "Hủy đơn" | `BUG-SHOP-0057_chrome-correct_20240316.png` |
| 3 | So sánh Safari vs Chrome (side by side) | `BUG-SHOP-0057_comparison_20240316.png` |
| 4 | Safari Console log — TypeError message | `BUG-SHOP-0057_safari-console-error_20240316.png` |
| 5 | Modal "Đang hủy" bị kẹt sau khi click xác nhận | `BUG-SHOP-0057_stuck-modal_20240316.png` |
| 6 | BrowserStack — Safari 15 iOS 15 cũng bị lỗi | `BUG-SHOP-0057_browserstack-safari15_20240316.png` |
| 7 | Video tái hiện toàn bộ flow lỗi trên iPhone 14 Pro | `BUG-SHOP-0057_reproduce-iphone14_20240316.mp4` |
| 8 | API Response đúng (can_cancel: false) | `BUG-SHOP-0057_api-response-correct_20240316.png` |

---

### 💬 GHI CHÚ CHO DEV

```
🔍 Nghi vấn nguyên nhân:
  - Console Safari ghi: "TypeError: order.can_cancel?.toString is not a function"
  - Safari 15 không hỗ trợ đầy đủ Optional Chaining (?.) với method calls
  - Có thể code đang dùng: order.can_cancel?.toString() === 'true'
    hoặc tương tự — gây lỗi trên Safari cũ
  - Conditional render của React/Vue component có thể đang
    fallback sai khi expression throw error

📍 Vị trí code có thể liên quan:
  - File: src/components/OrderCard/OrderCard.jsx (hoặc .vue)
  - Dòng render nút: tìm kiếm "can_cancel" hoặc "Hủy đơn"
  - File: src/pages/OrderHistory/OrderHistory.jsx
  - CSS: src/styles/OrderCard.scss (kiểm tra responsive 390px)

🐞 Lỗi kép cần fix:
  LỖI 1: Nút "Hủy đơn" hiển thị sai (chính)
    → Fix conditional rendering với can_cancel
  LỖI 2: Khi API từ chối hủy đơn, UI bị kẹt modal
    → Cần handle error từ API cancel call, show toast error + đóng modal

🔗 Ticket liên quan:
  - TASK-1187: Tạo trang Lịch sử đơn hàng (feature gốc)
  - BUG-SHOP-0051: Lỗi modal payment kẹt tương tự (đã fix — cùng pattern)

📊 Cross-browser test matrix:
  ┌─────────────────┬──────────┬──────────┐
  │ Browser         │ Status   │ Test on  │
  ├─────────────────┼──────────┼──────────┤
  │ Chrome 124      │ ✅ Đúng  │ Desktop  │
  │ Chrome 124 iOS  │ ✅ Đúng  │ iPhone   │
  │ Firefox 125     │ ✅ Đúng  │ Desktop  │
  │ Safari 17 iOS   │ ❌ Lỗi  │ iPhone   │
  │ Safari 16 iOS   │ ❌ Lỗi  │ BS       │
  │ Safari 15 iOS   │ ❌ Lỗi  │ BS       │
  │ Samsung Internet│ ✅ Đúng  │ Android  │
  └─────────────────┴──────────┴──────────┘

⚡ Gợi ý fix:
  1. Thay optional chaining với method call bằng cách check rõ ràng:
     // Thay vì:
     { order.can_cancel?.toString() === 'true' && <CancelButton /> }
     // Dùng:
     { order.can_cancel === true && <CancelButton /> }
  2. Hoặc thêm Babel plugin: @babel/plugin-proposal-optional-chaining
     với target: ['safari >= 14']
  3. Fix error handling trong hàm gọi API hủy đơn
  4. Viết E2E test với Playwright/Safari để bắt regression

📅 Deadline fix mong muốn: 2024-03-20
⚠️ Low-risk hotfix: chỉ sửa 1 điều kiện JS, không ảnh hưởng logic khác
```

---
---

## 📊 BẢNG QUY ƯỚC SEVERITY & PRIORITY

### Severity — Mức độ nghiêm trọng kỹ thuật

| Level | Ký hiệu | Mô tả | Ví dụ |
|---|:---:|---|---|
| **Critical** | 🔴 S1 | Hệ thống sập / mất dữ liệu / bảo mật | API crash, payment bị mất, SQL injection |
| **High** | 🟠 S2 | Tính năng core không dùng được | Không đăng nhập được, không tạo được đơn |
| **Medium** | 🟡 S3 | Tính năng bị ảnh hưởng nhưng có workaround | Hiển thị sai nhưng data đúng |
| **Low** | 🟢 S4 | Lỗi nhỏ, không ảnh hưởng chức năng | Typo, màu sắc sai, spacing lệch |

### Priority — Mức độ ưu tiên xử lý

| Level | Ký hiệu | Thời gian fix | Mô tả |
|---|:---:|:---:|---|
| **P1 — Urgent** | 🔴 | < 24h | Fix ngay, có thể hotfix production |
| **P2 — High** | 🟠 | < 3 ngày | Xử lý trong sprint hiện tại |
| **P3 — Medium** | 🟡 | < 2 tuần | Lên kế hoạch sprint tiếp theo |
| **P4 — Low** | 🟢 | Backlog | Xử lý khi có thời gian |

> 💡 **Lưu ý:** Severity và Priority có thể KHÁC NHAU.
> VD: Bug typo trên trang chủ (S4 — Low) nhưng ngay trước sự kiện lớn → P2 — High

---

## ✅ CHECKLIST TRƯỚC KHI SUBMIT

### Tester tự kiểm tra trước khi tạo bug

```
THÔNG TIN CƠ BẢN
☐ Bug ID được đặt đúng quy tắc
☐ Tiêu đề rõ ràng, đủ thông tin (Ai/Làm gì/Ở đâu/Sai như thế nào)
☐ Đã gán đúng Severity và Priority
☐ Đã gán cho đúng người

MÔI TRƯỜNG
☐ Đã ghi đầy đủ trình duyệt + phiên bản
☐ Đã ghi môi trường (Staging/UAT/Prod)
☐ Đã test cross-browser nếu nghi là UI bug
☐ Đã ghi rõ phiên bản app/build number

API BUG
☐ Đã có đủ: Method + URL + Headers + Body
☐ Đã paste response thực tế (status code + body)
☐ Đã ghi response mong đợi
☐ Đã kiểm tra: bug có tái hiện trên Postman thuần (không qua UI)?
☐ Đã che giấu token/password thật trong báo cáo
☐ Đã kèm Postman Collection export (nếu có)

UI BUG
☐ Bước tái hiện đủ chi tiết (người khác làm theo được)
☐ Đã ghi precondition rõ ràng
☐ Đã ghi dữ liệu test cụ thể
☐ Đã test: lỗi có phải do dữ liệu test đặc biệt không?

EVIDENCE
☐ Có ít nhất 1 screenshot rõ nét
☐ Ảnh đặt tên đúng quy tắc
☐ Có video nếu lỗi khó mô tả bằng chữ
☐ Có console log / network log nếu là lỗi JS/API

TRƯỚC KHI SUBMIT
☐ Đã tái hiện lại lần 2 để chắc chắn bug còn tồn tại
☐ Đã tìm kiếm duplicate (bug tương tự chưa được báo cáo?)
☐ Đã điền Expected và Actual Result rõ ràng
☐ Đã viết ghi chú cho dev (nếu có nghi vấn nguyên nhân)
☐ Spelling check — không có lỗi chính tả
```

---

> 📌 **Lưu ý cuối:**
> - **KHÔNG** paste token thật, password thật, thông tin user thật vào bug report
> - Bug report tốt = Dev có thể tái hiện và fix mà **không cần hỏi lại tester**
> - Cập nhật trạng thái bug khi có thay đổi
> - Sau khi dev fix → tester verify trên đúng môi trường và đúng build đã fix