Skip to content

QA Test Plan — Thu thập & Chuẩn hoá thông tin cá nhân khách hàng

Ref: PRD v1.0 | Date: 25/03/2026


D1) Test Scope

FRMô tảPriorityPlatform
FR-001Consent Screen — fullscreen, dynamic content, checkboxMustFlutter
FR-002Popup đề xuất cập nhật TT — birthday + occupation + provinceMustFlutter
FR-003Logic retry popup — hỏi lại có kiểm soátMustFlutter
FR-004Tích hợp auth flow — check consent + update_info sau loginMustFlutter
FR-005Track app_open_countMustFlutter
FR-006Admin: Config consent + profile updateMustAdmin Web
FR-007Admin: Thống kê % cập nhậtMustAdmin Web

D2) Test Cases

Happy path:

TCMô tảInputExpectedPriority
TC-001-01Khách mới thấy Consent Screen sau signupSignup thành công, consent_config version 1Hiện fullscreen Consent Screen với title, body, 2 checkboxes (mặc định checked)P0
TC-001-02Bấm "Đồng ý & Tiếp tục" với cả 2 checkbox checkedBấm CTALưu customer_consent record: consent_data = {"marketing": true, "treatment_photo": true}, consent_version = 1. Navigate tiếp (popup hoặc dashboard)P0
TC-001-03Bỏ 1 checkbox, bấm CTABỏ checkbox "Nhận thông tin khuyến mãi"Lưu consent_data = {"marketing": false, "treatment_photo": true}. Vẫn vào app bình thườngP0

Beyond happy path:

TCLoạiMô tảInputExpectedPriority
TC-001-04BoundaryBỏ cả 2 checkboxesBỏ tất cả checkbox, bấm CTALưu consent_data = {"marketing": false, "treatment_photo": false}. Vẫn vào appP1
TC-001-05NegativeKill app giữa Consent ScreenForce close app trước khi bấm CTALogin sau hiện lại Consent Screen (chưa có record)P1
TC-001-06Negativeconsent_config không tồn tạiXoá consent_config khỏi app_settingSkip Consent Screen, vào Dashboard bình thườngP1
TC-001-07NegativeMất mạng khi bấm CTATắt wifi trước khi bấmHiện toast lỗi, cho thử lại. Không navigateP1
TC-001-08NavigationBấm back (Android) hoặc swipe back (iOS)System back gestureKhông cho phép — vẫn ở Consent ScreenP1
TC-001-09LinkBấm "Chính sách bảo mật"Tap linkMở InAppWebView với URL từ app_setting.privacy_policy. Có nút Close quay lạiP2

TC-FR-002: Popup đề xuất cập nhật TT

Happy path:

TCMô tảInputExpectedPriority
TC-002-01Hiện popup 3 fields cho khách thiếu cả 3Khách chưa có birthday, occupation, provinceHiện popup với 3 fields: date picker, dropdown nghề nghiệp (4 options), dropdown tỉnh/thànhP0
TC-002-02Cập nhật cả 3 fieldsChọn birthday 15/06/1990, occupation "Kinh doanh tự do", tỉnh "Hồ Chí Minh". Bấm "Cập nhật"Lưu account.birthday, account.occupation, account_address.province_code. Set update_info_completed = true. Navigate DashboardP0
TC-002-03Chỉ hiện fields thiếuKhách đã có birthday, thiếu occupation + provincePopup chỉ hiện 2 fields: occupation + province. Không hiện birthdayP0

Beyond happy path:

TCLoạiMô tảInputExpectedPriority
TC-002-04BoundaryChỉ cập nhật 1 field, bỏ 2 fields trốngChỉ chọn birthday, bấm "Cập nhật"Lưu birthday. Lần sau popup chỉ hỏi occupation + provinceP1
TC-002-05BoundaryTất cả 3 fields đã có dataKhách đã có birthday + occupation + province_codeKhông hiện popup, vào Dashboard trực tiếpP0
TC-002-06NegativeBấm "Bỏ qua"Bấm "Bỏ qua"Tăng update_info_skip_count (+1). Navigate Dashboard. Không lưu dataP0
TC-002-07NegativeBấm nút X (close)Bấm X góc trên phảiTương tự bấm "Bỏ qua": tăng skip_count, navigate DashboardP1
TC-002-08CombinationCập nhật birthday hôm nayChọn ngày sinh = hôm nay (25/03/2026)Cho phép lưu (không validate tuổi)P2
TC-002-09CombinationChọn tỉnh/thành phố cuối danh sáchScroll xuống cuối dropdown, chọn tỉnhLưu đúng province_code + province_nameP2
TC-002-10NegativeMất mạng khi bấm "Cập nhật"Tắt wifiHiện toast lỗi, không navigate. Giữ data user đã nhậpP1

TC-FR-003: Logic retry popup

Happy path:

TCMô tảInputExpectedPriority
TC-003-01Hỏi lại sau 4 lần mở app (skip 1 lần)Skip lần 1 (skip_count=1). Mở app lần 2, 3, 4 (không hiện). Mở app lần 5 (app_open_count=4)Lần 5 hiện lại popup (4 >= 1*4)P0
TC-003-02Không hỏi nữa sau 3 lần skipSkip 3 lần (skip_count=3, max_skip=3)Không hiện popup nữa, bất kể mở app bao nhiêu lầnP0

Beyond happy path:

TCLoạiMô tảInputExpectedPriority
TC-003-03BoundarySkip 2 lần, hỏi lại sau 8 lần mởskip_count=2. Mở app 7 lần (không hiện). Mở lần 8Lần 8 hiện popup (8 >= 2*4)P1
TC-003-04CombinationAdmin thay đổi max_skip từ 3 thành 1Khách đã skip 1 lần. Admin set max_skip=1. Khách mở appKhông hiện popup nữa (1 >= 1)P2
TC-003-05CombinationAdmin thay đổi reshow_after_opens từ 4 thành 2Khách skip 1 lần. Admin set reshow_after_opens=2. Khách mở app lần 2Hiện popup (app_open >= 1*2)P2

TC-FR-004: Tích hợp auth flow

Happy path:

TCMô tảInputExpectedPriority
TC-004-01Khách mới: full flowSignup → auth successConsent Screen → Profile Update Popup → DashboardP0
TC-004-02Khách cũ chưa consentLogin → auth success. Chưa có customer_consent recordConsent Screen → Profile Update Popup → DashboardP0
TC-004-03Khách đã consent + đủ dataLogin. Có consent record (version current) + birthday + occupation + provinceThẳng Dashboard, không hiện gìP0

Beyond happy path:

TCLoạiMô tảInputExpectedPriority
TC-004-04CombinationAdmin tăng consent versionKhách có consent version=1. Admin tăng lên version=2Hiện Consent Screen lại (version cũ < current)P0
TC-004-05CombinationEvent popup + consentKhách cần consent + có event popupConsent Screen → Profile Popup → Dashboard → Event Popup (sau dashboard)P1
TC-004-06NegativeAPI lỗi khi load consentGetCustomerConsent trả lỗiSkip consent flow, vào Dashboard. Hiện toast lỗiP1

TC-FR-005: Track app_open_count

TCMô tảInputExpectedPriority
TC-005-01Increment mỗi lần mở appLogin 3 lần liên tiếpapp_open_count = 3 (tăng 1 mỗi lần)P0
TC-005-02Không increment nếu chưa consentLogin nhưng chưa có customer_consent recordapp_open_count không tồn tại (chưa có record)P1

TC-FR-006: Admin Config

Happy path:

TCMô tảInputExpectedPriority
TC-006-01Sửa consent title + bấm "Lưu"Sửa title → bấm "Lưu"app_setting cập nhật, version giữ nguyên. Khách KHÔNG bị hỏi lạiP0
TC-006-02Bấm "Lưu & Tăng version"Bấm nút → hiện dialog xác nhận → confirmversion++ trong app_setting. Khách phải consent lạiP0
TC-006-03Sửa occupation optionsThêm option "Nội trợ" vào danh sáchapp_setting cập nhật. Flutter app hiện option mới trong dropdownP0

Beyond happy path:

TCLoạiMô tảInputExpectedPriority
TC-006-04NegativeBấm "Lưu & Tăng version" rồi huỷ dialogBấm nút → hiện dialog → bấm "Huỷ"Không lưu, version không đổiP1
TC-006-05BoundaryToggle tắt popup đề xuấtTắt toggle "Bật popup đề xuất" → Lưuprofile_update_info.enabled = false. Flutter app skip popupP1
TC-006-06CombinationSửa max_skip thành 0Set max_skip = 0 → LưuPopup không bao giờ hiện (0 >= 0 ngay lập tức)P2

TC-FR-007: Admin Thống kê

TCMô tảInputExpectedPriority
TC-007-01Hiển thị 5 metricsMở tab Consent trên adminHiển thị: Tổng khách, Đã consent (%), Có birthday (%), Có occupation (%), Có tỉnh/thành (%)P0
TC-007-02% tính đúng100 khách, 70 đã consentĐã consent: 70 (70.0%)P1
TC-007-03Refresh khi mở tabĐóng/mở tab ConsentData load mới, không cacheP2

D3) Seed Data

Cách tạo: SQL Script

sql
-- Tạo 5 test accounts với trạng thái khác nhau

-- Account 1: Khách mới, chưa có gì
INSERT INTO account (id, display_name, phone_code, phone_number, role, gender)
VALUES ('test-consent-01', 'Nguyễn Thị Mới', 84, '0901000001', 'customer', 'female');

-- Account 2: Khách đã consent, chưa cập nhật TT
INSERT INTO account (id, display_name, phone_code, phone_number, role, gender)
VALUES ('test-consent-02', 'Trần Văn Cũ', 84, '0901000002', 'customer', 'male');
INSERT INTO customer_consent (account_id, branch_id, consent_data, consent_version, app_open_count)
VALUES ('test-consent-02', 'test-branch-id', '{"marketing": true, "treatment_photo": true}', 1, 0);

-- Account 3: Khách đã consent + skip 1 lần, mở app 3 lần
INSERT INTO account (id, display_name, phone_code, phone_number, role, gender)
VALUES ('test-consent-03', 'Lê Thị Skip', 84, '0901000003', 'customer', 'female');
INSERT INTO customer_consent (account_id, branch_id, consent_data, consent_version, update_info_skip_count, app_open_count)
VALUES ('test-consent-03', 'test-branch-id', '{"marketing": true, "treatment_photo": false}', 1, 1, 3);

-- Account 4: Khách đã cập nhật đủ thông tin
INSERT INTO account (id, display_name, phone_code, phone_number, role, gender, birthday, occupation)
VALUES ('test-consent-04', 'Phạm Văn Đủ', 84, '0901000004', 'customer', 'male', '1990-06-15', 'office_worker');
INSERT INTO customer_consent (account_id, branch_id, consent_data, consent_version, update_info_completed, app_open_count)
VALUES ('test-consent-04', 'test-branch-id', '{"marketing": true, "treatment_photo": true}', 1, true, 5);
INSERT INTO account_address (account_id, province_code, province_name, primary_contact)
VALUES ('test-consent-04', '79', 'Hồ Chí Minh', true);

-- Account 5: Khách skip 3 lần (max) — không hỏi nữa
INSERT INTO account (id, display_name, phone_code, phone_number, role, gender)
VALUES ('test-consent-05', 'Hoàng Thị Max', 84, '0901000005', 'customer', 'female');
INSERT INTO customer_consent (account_id, branch_id, consent_data, consent_version, update_info_skip_count, app_open_count)
VALUES ('test-consent-05', 'test-branch-id', '{"marketing": true, "treatment_photo": true}', 1, 3, 15);

-- Verify
SELECT a.id, a.display_name, a.birthday, a.occupation,
       cc.consent_version, cc.update_info_skip_count, cc.update_info_completed, cc.app_open_count,
       aa.province_code
FROM account a
LEFT JOIN customer_consent cc ON cc.account_id = a.id
LEFT JOIN account_address aa ON aa.account_id = a.id AND aa.primary_contact = true
WHERE a.id LIKE 'test-consent-%'
ORDER BY a.id;

D4) Traceability

FRTC-IDCoverageStatus
FR-001TC-001-01 ~ TC-001-099 test cases (3 happy + 6 beyond)
FR-002TC-002-01 ~ TC-002-1010 test cases (3 happy + 7 beyond)
FR-003TC-003-01 ~ TC-003-055 test cases (2 happy + 3 beyond)
FR-004TC-004-01 ~ TC-004-066 test cases (3 happy + 3 beyond)
FR-005TC-005-01 ~ TC-005-022 test cases
FR-006TC-006-01 ~ TC-006-066 test cases (3 happy + 3 beyond)
FR-007TC-007-01 ~ TC-007-033 test cases
Tổng41 test cases

D5) Entry / Exit Criteria

Entry (bắt đầu test)

  • [ ] BE deploy xong: migration customer_consent + seed data + Hasura metadata (track table + permissions)
  • [ ] Hasura metadata: public_account.yaml đã sửa (customer update birthday, occupation)
  • [ ] FE Web deploy xong trên test env (admin tab Consent)
  • [ ] Flutter build deploy xong trên test device/emulator
  • [ ] Seed data DS-001 đã chạy trên test DB
  • [ ] Test accounts có token/login credentials
  • [ ] consent_config + profile_update_info đã có trong app_setting

Exit (kết thúc test)

  • [ ] Tất cả P0 test cases PASS (19 TCs)
  • [ ] Tất cả P1 test cases PASS hoặc có waiver từ PO (15 TCs)
  • [ ] P2 test cases: document known issues nếu FAIL (7 TCs)
  • [ ] Performance: GetCustomerConsent < 100ms, ConsentStats < 500ms
  • [ ] No critical/major bugs open
  • [ ] Cross-platform test: Android + iOS cho Flutter screens