hoàn thành chức năng các test case chính
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,431 @@
|
|||||||
|
================================================================================
|
||||||
|
DANH SÁCH 49 KỊCH BẢN KIỂM THỬ - LARAVEL DUSK BROWSER TESTS
|
||||||
|
Dự án: Quay Số May Mắn - quayso.sisvietnam.vn
|
||||||
|
Tổng kết: 49 tests PASSED | 90 assertions | ~103 giây
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 1: tests/Browser/ExampleTest.php (1 test)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[TC-01] test_basic_example
|
||||||
|
Mô tả : Kiểm tra cơ bản trang chủ tải được vòng quay may mắn.
|
||||||
|
Bước : 1. Truy cập URL gốc "/".
|
||||||
|
Kết quả: Phần tử #wheelCanvas (canvas vòng quay) hiển thị trên trang.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 2: tests/Browser/GamePageTest.php (12 tests)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[TC-02] test_game_page_loads_when_guest
|
||||||
|
Mô tả : Trang game tải được khi người dùng chưa đăng nhập.
|
||||||
|
Bước : 1. Truy cập "/game" với vai trò khách (guest).
|
||||||
|
Kết quả: URL path là "/game" (trang không bị redirect).
|
||||||
|
|
||||||
|
[TC-03] test_root_redirects_to_game
|
||||||
|
Mô tả : URL gốc "/" hiển thị cùng nội dung với "/game".
|
||||||
|
Bước : 1. Truy cập "/".
|
||||||
|
Kết quả: Phần tử #wheelCanvas (canvas vòng quay) hiển thị.
|
||||||
|
|
||||||
|
[TC-04] test_game_page_shows_lucky_wheel_canvas
|
||||||
|
Mô tả : Canvas vòng quay may mắn hiển thị trên trang game.
|
||||||
|
Bước : 1. Truy cập "/game".
|
||||||
|
Kết quả: Phần tử #wheelCanvas hiển thị trong trang.
|
||||||
|
|
||||||
|
[TC-05] test_spin_button_is_present
|
||||||
|
Mô tả : Nút "Quay" hiển thị trên trang game.
|
||||||
|
Bước : 1. Truy cập "/game".
|
||||||
|
Kết quả: Phần tử #spinButton hiển thị trong trang.
|
||||||
|
|
||||||
|
[TC-06] test_game_rule_ribbon_is_present
|
||||||
|
Mô tả : Banner "Thể lệ tham dự" hiển thị trên trang game.
|
||||||
|
Bước : 1. Truy cập "/game".
|
||||||
|
Kết quả: Phần tử #game_rule hiển thị trong trang.
|
||||||
|
|
||||||
|
[TC-07] test_guest_sees_login_prompt_on_game
|
||||||
|
Mô tả : Khách chưa đăng nhập nhìn thấy thông báo yêu cầu đăng nhập.
|
||||||
|
Bước : 1. Đăng xuất ("/logout").
|
||||||
|
2. Truy cập "/game".
|
||||||
|
Kết quả: Thông báo ".alert_wheel_spin" hiển thị;
|
||||||
|
Link đăng nhập (href chứa "login") hiển thị.
|
||||||
|
|
||||||
|
[TC-08] test_logged_in_user_can_see_spin_button_enabled
|
||||||
|
Mô tả : Người dùng đã đăng nhập và chưa quay thấy nút quay ở trạng thái sẵn sàng.
|
||||||
|
Bước : 1. Đăng xuất và vào "/login".
|
||||||
|
2. Nhập thông tin cá nhân (họ tên, SĐT mới, địa chỉ, nguồn biết SIS, nghề nghiệp).
|
||||||
|
3. Nhấn "Gửi thông tin" và chờ redirect về "/game".
|
||||||
|
Kết quả: Thuộc tính data-check của #spinButton = "true" (nút quay được bật).
|
||||||
|
Dọn dẹp: Xóa user vừa tạo khỏi DB.
|
||||||
|
|
||||||
|
[TC-09] test_already_spined_user_sees_spin_button_disabled
|
||||||
|
Mô tả : Người dùng đã quay (spined=1) thấy nút quay bị khóa.
|
||||||
|
Chuẩn bị: Tạo user với spined=1 trực tiếp trong DB.
|
||||||
|
Bước : 1. Đăng nhập bằng user có spined=1.
|
||||||
|
2. Truy cập "/game".
|
||||||
|
Kết quả: Thuộc tính data-check của #spinButton = "false" (nút quay bị khóa).
|
||||||
|
Dọn dẹp: Xóa user test khỏi DB.
|
||||||
|
|
||||||
|
[TC-10] test_game_rule_popup_loads
|
||||||
|
Mô tả : Popup thể lệ tham dự tải nội dung khi nhấn banner.
|
||||||
|
Bước : 1. Truy cập "/game".
|
||||||
|
2. Xóa các element ".alert" bằng JavaScript (để không che banner).
|
||||||
|
3. Nhấn vào #game_rule bằng JavaScript click.
|
||||||
|
4. Chờ 800ms.
|
||||||
|
Kết quả: Phần tử #gamerule (popup) hiển thị trong trang.
|
||||||
|
|
||||||
|
[TC-11] test_game_rule_api_returns_html
|
||||||
|
Mô tả : API /game/rule trả về nội dung HTML (dùng cho AJAX).
|
||||||
|
Bước : 1. Truy cập "/game/rule" trực tiếp trên trình duyệt.
|
||||||
|
Kết quả: Source HTML của trang chứa chuỗi "class=" (là fragment HTML hợp lệ).
|
||||||
|
|
||||||
|
[TC-12] test_checkspined_returns_error_for_guest
|
||||||
|
Mô tả : API /checkspined trả lỗi khi người dùng chưa đăng nhập.
|
||||||
|
Bước : 1. Đăng xuất ("/logout").
|
||||||
|
2. Truy cập "/checkspined".
|
||||||
|
Kết quả: Trang hiển thị chuỗi "error".
|
||||||
|
|
||||||
|
[TC-13] test_checkspined_returns_zero_for_new_user
|
||||||
|
Mô tả : API /checkspined trả spined=0 cho người dùng mới chưa quay.
|
||||||
|
Chuẩn bị: Tạo user mới với spined=0 trong DB.
|
||||||
|
Bước : 1. Đăng nhập bằng user đó.
|
||||||
|
2. Truy cập "/checkspined".
|
||||||
|
Kết quả: Trang hiển thị chuỗi '"spined":0'.
|
||||||
|
Dọn dẹp: Xóa user test khỏi DB.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 3: tests/Browser/CreateAccountTest.php (5 tests)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[TC-14] test_login_page_shows_form
|
||||||
|
Mô tả : Trang đăng ký/đăng nhập hiển thị đủ các trường nhập liệu.
|
||||||
|
Bước : 1. Đăng xuất để đảm bảo trạng thái guest.
|
||||||
|
2. Truy cập "/login".
|
||||||
|
Kết quả: Tiêu đề "Nhập thông tin cá nhân" hiển thị;
|
||||||
|
Các trường input[name="name"], input[name="phone"],
|
||||||
|
input[name="address"], input[name="job"] đều có mặt.ss
|
||||||
|
|
||||||
|
[TC-15] test_empty_form_shows_validation_errors
|
||||||
|
Mô tả : Gửi form rỗng phải hiện lỗi validation từ server.
|
||||||
|
Bước : 1. Đăng xuất và truy cập "/login".
|
||||||
|
2. Nhấn "Gửi thông tin" mà không nhập gì.
|
||||||
|
3. Chờ phần tử ".text-danger" xuất hiện.
|
||||||
|
Kết quả: Ít nhất một phần tử ".text-danger" (thông báo lỗi) hiển thị.
|
||||||
|
|
||||||
|
[TC-16] test_invalid_phone_shows_error
|
||||||
|
Mô tả : Số điện thoại sai định dạng phải hiện thông báo lỗi.
|
||||||
|
Bước : 1. Truy cập "/login".
|
||||||
|
2. Nhập tên hợp lệ, nhập SĐT = "12345" (sai định dạng).
|
||||||
|
3. Điền các trường còn lại và nhấn "Gửi thông tin".
|
||||||
|
Kết quả: Trang hiển thị "Số điện thoại không đúng định dạng".
|
||||||
|
|
||||||
|
[TC-17] test_successful_registration_redirects_to_game
|
||||||
|
Mô tả : Đăng ký thành công tạo tài khoản mới và chuyển về trang game.
|
||||||
|
Bước : 1. Truy cập "/login".
|
||||||
|
2. Nhập: tên="Test Dusk User", SĐT=số ngẫu nhiên (09xxxxxxxx),
|
||||||
|
địa chỉ="Cần Thơ", nguồn biết SIS=1, nghề nghiệp="Kỹ sư".
|
||||||
|
3. Nhấn "Gửi thông tin" và chờ thông báo "thành công".
|
||||||
|
Kết quả: URL path là "/game" (đăng ký thành công, redirect về game).
|
||||||
|
DB Check: User có phone tương ứng tồn tại trong DB;
|
||||||
|
name = "TEST DUSK USER" (đã uppercase);
|
||||||
|
address = "Cần Thơ"; known_sis = 1; job = "Kỹ sư";
|
||||||
|
email = "<phone>@phone.local"; spined = 0; status = 1.
|
||||||
|
Dọn dẹp: Xóa user test khỏi DB.
|
||||||
|
|
||||||
|
[TC-18] test_authenticated_user_sees_account_page
|
||||||
|
Mô tả : Người dùng đã đăng nhập truy cập /login sẽ thấy trang tài khoản của họ.
|
||||||
|
Bước : 1. Đăng ký tài khoản mới (SĐT ngẫu nhiên).
|
||||||
|
2. Sau khi đăng nhập, truy cập lại "/login".
|
||||||
|
Kết quả: Trường phone được điền sẵn số điện thoại của người dùng
|
||||||
|
(chứng tỏ đang hiển thị form cập nhật tài khoản, không phải form đăng ký).
|
||||||
|
DB Check: User tồn tại; spined = 0; status = 1.
|
||||||
|
Dọn dẹp: Xóa user test khỏi DB.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 4: tests/Browser/AccountManagementTest.php (5 tests)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[TC-19] test_update_account_successfully
|
||||||
|
Mô tả : Người dùng cập nhật thông tin tài khoản thành công.
|
||||||
|
Bước : 1. Đăng ký và đăng nhập tài khoản mới.
|
||||||
|
2. Truy cập "/login" (trang tài khoản).
|
||||||
|
3. Xóa và nhập lại: tên="Tên Mới Test", địa chỉ="Hà Nội",
|
||||||
|
nguồn biết SIS=0, nghề nghiệp="Giáo viên".
|
||||||
|
4. Nhấn "Cập nhật" và chờ thông báo "thành công".
|
||||||
|
Kết quả: URL path là "/game" (cập nhật thành công).
|
||||||
|
DB Check: name = "TÊN MỚI TEST" (đã uppercase); address = "Hà Nội";
|
||||||
|
known_sis = 0; job = "Giáo viên".
|
||||||
|
Dọn dẹp: Xóa user test khỏi DB.
|
||||||
|
|
||||||
|
[TC-20] test_update_account_with_empty_name_shows_error
|
||||||
|
Mô tả : Cập nhật tài khoản với tên rỗng phải hiện lỗi validation.
|
||||||
|
Bước : 1. Đăng ký và đăng nhập tài khoản mới.
|
||||||
|
2. Truy cập "/login", xóa trường tên.
|
||||||
|
3. Nhấn "Cập nhật".
|
||||||
|
Kết quả: Phần tử ".text-danger" (thông báo lỗi) hiển thị.
|
||||||
|
|
||||||
|
[TC-21] test_update_account_with_invalid_phone_shows_error
|
||||||
|
Mô tả : Cập nhật tài khoản với số điện thoại sai định dạng phải hiện lỗi.
|
||||||
|
Bước : 1. Đăng ký và đăng nhập tài khoản mới.
|
||||||
|
2. Truy cập "/login", xóa và nhập SĐT = "abcde".
|
||||||
|
3. Nhấn "Cập nhật".
|
||||||
|
Kết quả: Trang hiển thị "Số điện thoại không đúng định dạng".
|
||||||
|
|
||||||
|
[TC-22] test_history_section_visible_when_authenticated
|
||||||
|
Mô tả : Phần lịch sử hoạt động hiển thị khi người dùng đã đăng nhập.
|
||||||
|
Bước : 1. Đăng ký và đăng nhập tài khoản mới.
|
||||||
|
2. Truy cập "/login" (trang tài khoản).
|
||||||
|
Kết quả: Trang hiển thị "LỊCH SỬ HOẠT ĐỘNG".
|
||||||
|
|
||||||
|
[TC-23] test_logout_works
|
||||||
|
Mô tả : Đăng xuất thành công xóa session người dùng.
|
||||||
|
Bước : 1. Đăng ký và đăng nhập tài khoản mới.
|
||||||
|
2. Truy cập "/logout".
|
||||||
|
Kết quả: Người dùng ở trạng thái guest (assertGuest).
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 5: tests/Browser/AdminLoginTest.php (7 tests)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Chuẩn bị chung: Trước mỗi test tạo admin user với đầy đủ roles trong DB.
|
||||||
|
Dọn dẹp chung: Sau mỗi test xóa UserRole và admin user khỏi DB.
|
||||||
|
|
||||||
|
[TC-24] test_admin_login_page_shows_form
|
||||||
|
Mô tả : Trang quản trị "/admin" hiển thị form đăng nhập.
|
||||||
|
Bước : 1. Truy cập "/admin".
|
||||||
|
Kết quả: Các trường input[name="email"], input[name="password"],
|
||||||
|
input[type="submit"] đều hiển thị.
|
||||||
|
|
||||||
|
[TC-25] test_admin_login_with_wrong_credentials_shows_error
|
||||||
|
Mô tả : Đăng nhập admin với thông tin sai phải hiện thông báo lỗi.
|
||||||
|
Bước : 1. Truy cập "/admin".
|
||||||
|
2. Nhập email="wrong@email.com", password="wrongpassword".
|
||||||
|
3. Nhấn "Login".
|
||||||
|
Kết quả: Phần tử ".alert-danger" (hoặc tương đương) hiển thị.
|
||||||
|
|
||||||
|
[TC-26] test_admin_login_with_empty_fields_shows_validation
|
||||||
|
Mô tả : Gửi form đăng nhập admin rỗng phải hiện lỗi validation server-side.
|
||||||
|
Bước : 1. Truy cập "/admin".
|
||||||
|
2. Dùng JavaScript xóa thuộc tính "required" khỏi tất cả input
|
||||||
|
(để bypass validation HTML5 của trình duyệt).
|
||||||
|
3. Nhấn "Login" mà không nhập gì.
|
||||||
|
Kết quả: Phần tử ".text-danger" hoặc ".alert-danger" hiển thị.
|
||||||
|
|
||||||
|
[TC-27] test_admin_login_successful
|
||||||
|
Mô tả : Đăng nhập admin thành công và chuyển vào trang quản lý.
|
||||||
|
Bước : 1. Đăng xuất admin ("/admin/logout").
|
||||||
|
2. Truy cập "/admin", nhập email và password đúng.
|
||||||
|
3. Nhấn "Login" và chờ redirect.
|
||||||
|
Kết quả: URL path là "/admin/account" (đăng nhập thành công).
|
||||||
|
|
||||||
|
[TC-28] test_authenticated_admin_visiting_login_sees_form
|
||||||
|
Mô tả : Admin đã đăng nhập truy cập "/admin" vẫn thấy form login (không redirect).
|
||||||
|
Bước : 1. Đăng nhập bằng loginAs(adminUser).
|
||||||
|
2. Truy cập "/admin".
|
||||||
|
Kết quả: URL path vẫn là "/admin"; form input[name="email"] hiển thị.
|
||||||
|
|
||||||
|
[TC-29] test_unauthenticated_access_to_admin_account_redirects
|
||||||
|
Mô tả : Truy cập trang admin khi chưa đăng nhập bị redirect về trang login.
|
||||||
|
Bước : 1. Đăng xuất admin.
|
||||||
|
2. Truy cập "/admin/account" trực tiếp.
|
||||||
|
Kết quả: URL path là "/admin" (bị redirect về trang đăng nhập admin).
|
||||||
|
|
||||||
|
[TC-30] test_admin_logout
|
||||||
|
Mô tả : Đăng xuất admin thành công — sau đó không thể vào trang quản lý.
|
||||||
|
Bước : 1. Đăng xuất admin (clear session cũ).
|
||||||
|
2. Đăng nhập thực qua form với email/password đúng.
|
||||||
|
3. Chờ redirect vào "/admin/account".
|
||||||
|
4. Đăng xuất qua "/logout" (frontend route).
|
||||||
|
5. Truy cập "/admin/account".
|
||||||
|
Kết quả: URL path là "/admin" (bị redirect về login, xác nhận đã logout).
|
||||||
|
Lưu ý : Dùng đăng nhập form thực (không dùng loginAs) để tránh Dusk
|
||||||
|
tự động re-authenticate sau khi logout.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 6: tests/Browser/AdminPrizeTest.php (9 tests)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Chuẩn bị chung: Tạo admin user với đầy đủ roles trong DB.
|
||||||
|
Dọn dẹp chung: Xóa UserRole và admin user khỏi DB.
|
||||||
|
|
||||||
|
[TC-31] test_prize_list_page_loads
|
||||||
|
Mô tả : Trang danh sách giải thưởng tải thành công.
|
||||||
|
Bước : 1. Đăng nhập admin.
|
||||||
|
2. Truy cập "/admin/prizes".
|
||||||
|
Kết quả: Phần tử "table" hoặc ".table" hiển thị.
|
||||||
|
|
||||||
|
[TC-32] test_add_prize_form_loads
|
||||||
|
Mô tả : Trang thêm mới giải thưởng hiển thị đầy đủ form.
|
||||||
|
Bước : 1. Đăng nhập admin.
|
||||||
|
2. Truy cập "/admin/prize/update?id=0".
|
||||||
|
Kết quả: Các trường input[name="prize_name"], input[name="prize_order"],
|
||||||
|
select[name="prize_status"] đều hiển thị.
|
||||||
|
|
||||||
|
[TC-33] test_add_prize_with_empty_name_shows_error
|
||||||
|
Mô tả : Gửi form thêm giải thưởng với tên rỗng phải hiện lỗi validation.
|
||||||
|
Bước : 1. Đăng nhập admin, vào "/admin/prize/update?id=0".
|
||||||
|
2. Dùng JavaScript xóa thuộc tính "required" của các input.
|
||||||
|
3. Nhấn "Thêm mới" mà không nhập tên.
|
||||||
|
Kết quả: Phần tử ".text-danger", ".alert-danger" hoặc tương đương hiển thị.
|
||||||
|
|
||||||
|
[TC-34] test_create_prize_successfully
|
||||||
|
Mô tả : Thêm mới giải thưởng thành công và dữ liệu được lưu vào DB.
|
||||||
|
Bước : 1. Đăng nhập admin, vào "/admin/prize/update?id=0".
|
||||||
|
2. Nhập: prize_name=tên ngẫu nhiên, prize_number=100,
|
||||||
|
prize_order=99, prize_status=1.
|
||||||
|
3. Nhấn "Thêm mới" và chờ ".alert-success".
|
||||||
|
Kết quả: Thông báo thành công hiển thị.
|
||||||
|
DB Check: Prize với tên vừa nhập tồn tại trong DB;
|
||||||
|
prize_status = 1; prize_number = 100; prize_order = 99.
|
||||||
|
Dọn dẹp: Xóa prize test khỏi DB.
|
||||||
|
|
||||||
|
[TC-35] test_update_prize_successfully
|
||||||
|
Mô tả : Cập nhật tên giải thưởng thành công.
|
||||||
|
Chuẩn bị: Tạo prize "Dusk Prize To Update" trực tiếp trong DB.
|
||||||
|
Bước : 1. Đăng nhập admin, vào "/admin/prize/update?id={prizeId}".
|
||||||
|
2. Xóa và nhập tên mới vào prize_name.
|
||||||
|
3. Nhấn "Cập nhật" và chờ ".alert-success".
|
||||||
|
Kết quả: Thông báo thành công hiển thị.
|
||||||
|
DB Check: Prize::find(id)->prize_name bằng tên mới vừa nhập.
|
||||||
|
Dọn dẹp: Xóa prize test khỏi DB.
|
||||||
|
|
||||||
|
[TC-36] test_delete_prize_successfully
|
||||||
|
Mô tả : Xóa giải thưởng thành công khỏi hệ thống.
|
||||||
|
Chuẩn bị: Tạo prize "Dusk Prize To Delete" trực tiếp trong DB.
|
||||||
|
Bước : 1. Đăng nhập admin, vào "/admin/prizes".
|
||||||
|
2. Truy cập "/admin/prize/delete/{prizeId}" trực tiếp.
|
||||||
|
3. Chờ 1000ms (hệ thống redirect sau khi xóa).
|
||||||
|
Kết quả: Prize::find(prizeId) trả về null (đã bị xóa khỏi DB).
|
||||||
|
|
||||||
|
[TC-37] test_prize_winner_list_loads
|
||||||
|
Mô tả : Trang danh sách người trúng giải tải thành công.
|
||||||
|
Bước : 1. Đăng nhập admin.
|
||||||
|
2. Truy cập "/admin/prizeuser".
|
||||||
|
Kết quả: Phần tử "table" hoặc ".table" hiển thị.
|
||||||
|
|
||||||
|
[TC-38] test_prize_winner_list_has_date_filter
|
||||||
|
Mô tả : Trang danh sách người trúng giải có bộ lọc theo ngày.
|
||||||
|
Bước : 1. Đăng nhập admin, truy cập "/admin/prizeuser".
|
||||||
|
Kết quả: Phần tử #fromDateTime và #toDateTime hiển thị.
|
||||||
|
|
||||||
|
[TC-39] test_prize_winner_export_link_present
|
||||||
|
Mô tả : Trang danh sách người trúng giải có nút/link xuất Excel.
|
||||||
|
Bước : 1. Đăng nhập admin, truy cập "/admin/prizeuser".
|
||||||
|
Kết quả: Phần tử #excel_export hiển thị.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 7: tests/Browser/AdminUserTest.php (5 tests)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Chuẩn bị chung: Tạo admin user (với đầy đủ roles) và target user trong DB.
|
||||||
|
Dọn dẹp chung: Xóa UserRole, admin user và target user khỏi DB.
|
||||||
|
|
||||||
|
[TC-40] test_user_list_page_loads
|
||||||
|
Mô tả : Trang danh sách người dùng trong admin tải thành công.
|
||||||
|
Bước : 1. Đăng nhập admin.
|
||||||
|
2. Truy cập "/admin/users".
|
||||||
|
Kết quả: Phần tử "table" hoặc ".table" hiển thị.
|
||||||
|
|
||||||
|
[TC-41] test_user_list_shows_target_user
|
||||||
|
Mô tả : User mới tạo xuất hiện trong danh sách quản lý người dùng.
|
||||||
|
Bước : 1. Truy vấn DB: User với status != '' (điều kiện của controller).
|
||||||
|
2. Đăng nhập admin, truy cập "/admin/users", chờ #table.
|
||||||
|
Kết quả: Target user tồn tại trong query DB (email khớp);
|
||||||
|
Bảng #table hiển thị trên trang.
|
||||||
|
Lưu ý : DataTables có phân trang; dùng DB assertion thay vì tìm kiếm text
|
||||||
|
trực tiếp trên trình duyệt.
|
||||||
|
|
||||||
|
[TC-42] test_edit_user_form_shows_existing_data
|
||||||
|
Mô tả : Form cập nhật user điền sẵn thông tin hiện tại.
|
||||||
|
Bước : 1. Đăng nhập admin.
|
||||||
|
2. Truy cập "/admin/users/update?id={targetUserId}".
|
||||||
|
Kết quả: Trường input "name" có giá trị = tên hiện tại của user;
|
||||||
|
Trường input "email" có giá trị = email hiện tại của user.
|
||||||
|
|
||||||
|
[TC-43] test_update_user_name_successfully
|
||||||
|
Mô tả : Admin cập nhật tên người dùng thành công (không uppercase).
|
||||||
|
Bước : 1. Đăng nhập admin, vào "/admin/users/update?id={targetUserId}".
|
||||||
|
2. Xóa và nhập tên mới = "Updated Dusk Name".
|
||||||
|
3. Nhấn "Cập nhật" và chờ ".alert-success".
|
||||||
|
Kết quả: Thông báo thành công hiển thị.
|
||||||
|
DB Check: User::find(id)->name = "Updated Dusk Name"
|
||||||
|
(Admin controller KHÔNG uppercase tên, khác với frontend controller).
|
||||||
|
|
||||||
|
[TC-44] test_admin_account_page_shows_user_info
|
||||||
|
Mô tả : Trang tài khoản admin hiển thị đúng thông tin người dùng đang đăng nhập.
|
||||||
|
Bước : 1. Đăng nhập admin.
|
||||||
|
2. Truy cập "/admin/account".
|
||||||
|
Kết quả: Trường input "email" có giá trị = email của admin đang đăng nhập.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
FILE 8: tests/Browser/AdminParameterTest.php (5 tests)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Chuẩn bị chung: Tạo admin user với đầy đủ roles trong DB.
|
||||||
|
Dọn dẹp chung: Xóa UserRole và admin user khỏi DB.
|
||||||
|
|
||||||
|
[TC-45] test_parameter_page_loads
|
||||||
|
Mô tả : Trang cấu hình hệ thống (parameter) tải thành công.
|
||||||
|
Bước : 1. Đăng nhập admin.
|
||||||
|
2. Truy cập "/admin/parameter".
|
||||||
|
Kết quả: Phần tử "form" hiển thị trong trang.
|
||||||
|
|
||||||
|
[TC-46] test_parameter_page_shows_game_rule_field
|
||||||
|
Mô tả : Trang cấu hình có trường nhập "Thể lệ tham dự" (game_rule).
|
||||||
|
Bước : 1. Đăng nhập admin, truy cập "/admin/parameter".
|
||||||
|
Kết quả: Phần tử [name="game_rule"] hoặc textarea hiển thị.
|
||||||
|
|
||||||
|
[TC-47] test_update_game_rule_successfully
|
||||||
|
Mô tả : Cập nhật nội dung thể lệ tham dự thành công và lưu vào DB.
|
||||||
|
Chuẩn bị: Đọc và lưu giá trị game_rule hiện tại từ DB.
|
||||||
|
Bước : 1. Đăng nhập admin, truy cập "/admin/parameter".
|
||||||
|
2. Dùng JavaScript gán giá trị mới vào [name="game_rule"]
|
||||||
|
(hỗ trợ cả CKEditor nếu có).
|
||||||
|
3. Nhấn "Cập nhật" và chờ ".alert-success".
|
||||||
|
Kết quả: Thông báo thành công hiển thị.
|
||||||
|
DB Check: Parameter với key="game_rule" tồn tại;
|
||||||
|
parameter_value chứa chuỗi vừa nhập (assertStringContainsString).
|
||||||
|
Dọn dẹp: Khôi phục giá trị game_rule gốc trong DB.
|
||||||
|
|
||||||
|
[TC-48] test_parameter_page_has_image_upload_fields
|
||||||
|
Mô tả : Trang cấu hình có trường upload ảnh logo.
|
||||||
|
Bước : 1. Đăng nhập admin, truy cập "/admin/parameter".
|
||||||
|
Kết quả: Phần tử input[type="file"][name="logo_image"] hoặc
|
||||||
|
input[name="logo_image"] hiển thị.
|
||||||
|
|
||||||
|
[TC-49] test_parameter_page_requires_auth
|
||||||
|
Mô tả : Truy cập "/admin/parameter" khi chưa đăng nhập bị redirect.
|
||||||
|
Bước : 1. Đăng xuất admin ("/admin/logout").
|
||||||
|
2. Truy cập "/admin/parameter" trực tiếp.
|
||||||
|
Kết quả: URL path là "/admin" (bị redirect về trang đăng nhập admin).
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
TỔNG KẾT
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
Số file test : 8 files
|
||||||
|
Số test cases : 49 test cases
|
||||||
|
Số assertions : 90 assertions
|
||||||
|
Thời gian : ~103 giây
|
||||||
|
Kết quả : ALL PASSED ✓
|
||||||
|
|
||||||
|
Phân bổ theo chức năng:
|
||||||
|
┌──────────────────────────────────────────┬────────┐
|
||||||
|
│ Chức năng │ Tests │
|
||||||
|
├──────────────────────────────────────────┼────────┤
|
||||||
|
│ ExampleTest (sanity check) │ 1 │
|
||||||
|
│ Trang game & API vòng quay │ 12 │
|
||||||
|
│ Đăng ký tài khoản │ 5 │
|
||||||
|
│ Quản lý tài khoản người dùng │ 5 │
|
||||||
|
│ Đăng nhập / đăng xuất admin │ 7 │
|
||||||
|
│ Quản lý giải thưởng (admin) │ 9 │
|
||||||
|
│ Quản lý người dùng (admin) │ 5 │
|
||||||
|
│ Cấu hình hệ thống (admin) │ 5 │
|
||||||
|
├──────────────────────────────────────────┼────────┤
|
||||||
|
│ TỔNG CỘNG │ 49 │
|
||||||
|
└──────────────────────────────────────────┴────────┘
|
||||||
|
|
||||||
|
Công nghệ kiểm thử:
|
||||||
|
- Framework : Laravel Dusk v8.5 (Browser Automation Testing)
|
||||||
|
- Browser : Google Chrome 146 (non-snap, /usr/bin/google-chrome)
|
||||||
|
- Driver : ChromeDriver 146
|
||||||
|
- Môi trường : WSL2 + WSLg, DUSK_HEADLESS_DISABLED=true
|
||||||
|
- Server : php artisan serve --port=8001
|
||||||
|
- DB : MySQL (fsg3kct926qj_quaysoDB)
|
||||||
|
|
||||||
|
================================================================================
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Tests\Browser;
|
namespace Tests\Browser;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Laravel\Dusk\Browser;
|
use Laravel\Dusk\Browser;
|
||||||
use Tests\DuskTestCase;
|
use Tests\DuskTestCase;
|
||||||
|
|
||||||
@@ -12,13 +13,16 @@ class AccountManagementTest extends DuskTestCase
|
|||||||
*/
|
*/
|
||||||
private function registerAndLogin(Browser $browser, string $phone): void
|
private function registerAndLogin(Browser $browser, string $phone): void
|
||||||
{
|
{
|
||||||
|
$browser->visit('/logout');
|
||||||
$browser->visit('/login')
|
$browser->visit('/login')
|
||||||
|
->pause(300)
|
||||||
->type('name', 'Dusk Test User')
|
->type('name', 'Dusk Test User')
|
||||||
->type('phone', $phone)
|
->type('phone', $phone)
|
||||||
->type('address', 'Cần Thơ')
|
->type('address', 'Cần Thơ')
|
||||||
->radio('known_sis', '1')
|
->radio('known_sis', '1')
|
||||||
->type('job', 'Nhân viên văn phòng')
|
->type('job', 'Nhân viên văn phòng')
|
||||||
->press('Gửi thông tin')
|
->press('Gửi thông tin')
|
||||||
|
->waitForText('thành công', 10)
|
||||||
->assertPathIs('/game');
|
->assertPathIs('/game');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +37,8 @@ class AccountManagementTest extends DuskTestCase
|
|||||||
$this->registerAndLogin($browser, $phone);
|
$this->registerAndLogin($browser, $phone);
|
||||||
|
|
||||||
$browser->visit('/login')
|
$browser->visit('/login')
|
||||||
->assertSee('Cập nhật thông tin cá nhân')
|
->pause(300)
|
||||||
|
->assertInputValue('phone', $phone)
|
||||||
->clear('name')
|
->clear('name')
|
||||||
->type('name', 'Tên Mới Test')
|
->type('name', 'Tên Mới Test')
|
||||||
->clear('address')
|
->clear('address')
|
||||||
@@ -42,9 +47,20 @@ class AccountManagementTest extends DuskTestCase
|
|||||||
->clear('job')
|
->clear('job')
|
||||||
->type('job', 'Giáo viên')
|
->type('job', 'Giáo viên')
|
||||||
->press('Cập nhật')
|
->press('Cập nhật')
|
||||||
->assertPathIs('/game')
|
->waitForText('thành công', 10)
|
||||||
->assertSee('Cập nhật tài khoản thành công');
|
->assertPathIs('/game');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB: dữ liệu đã được cập nhật đúng
|
||||||
|
$user = User::where('phone', $phone)->first();
|
||||||
|
$this->assertNotNull($user);
|
||||||
|
$this->assertEquals('TÊN MỚI TEST', $user->name, 'Tên phải uppercase');
|
||||||
|
$this->assertEquals('Hà Nội', $user->address);
|
||||||
|
$this->assertEquals(0, $user->known_sis);
|
||||||
|
$this->assertEquals('Giáo viên', $user->job);
|
||||||
|
|
||||||
|
// Dọn dẹp
|
||||||
|
$user->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,7 +76,8 @@ class AccountManagementTest extends DuskTestCase
|
|||||||
$browser->visit('/login')
|
$browser->visit('/login')
|
||||||
->clear('name')
|
->clear('name')
|
||||||
->press('Cập nhật')
|
->press('Cập nhật')
|
||||||
->assertSee('Nhập tên hiển thị');
|
->pause(300)
|
||||||
|
->assertPresent('.text-danger');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +95,7 @@ class AccountManagementTest extends DuskTestCase
|
|||||||
->clear('phone')
|
->clear('phone')
|
||||||
->type('phone', 'abcde')
|
->type('phone', 'abcde')
|
||||||
->press('Cập nhật')
|
->press('Cập nhật')
|
||||||
|
->pause(500)
|
||||||
->assertSee('Số điện thoại không đúng định dạng');
|
->assertSee('Số điện thoại không đúng định dạng');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Browser;
|
||||||
|
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\UserRole;
|
||||||
|
use Laravel\Dusk\Browser;
|
||||||
|
use Tests\DuskTestCase;
|
||||||
|
|
||||||
|
class AdminLoginTest extends DuskTestCase
|
||||||
|
{
|
||||||
|
private User $adminUser;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
// Tạo admin test user với đầy đủ roles
|
||||||
|
$this->adminUser = User::create([
|
||||||
|
'name' => 'DUSK ADMIN TEST',
|
||||||
|
'email' => 'dusk_admin_test@test.local',
|
||||||
|
'phone' => '09' . rand(10000000, 99999999),
|
||||||
|
'password' => bcrypt('dusk_admin_pwd_2026'),
|
||||||
|
'status' => 1,
|
||||||
|
'address' => 'Test',
|
||||||
|
'known_sis'=> 0,
|
||||||
|
'job' => 'Admin',
|
||||||
|
'spined' => 0,
|
||||||
|
]);
|
||||||
|
// Gán tất cả roles
|
||||||
|
$roles = Role::where('role_status', 1)->get();
|
||||||
|
foreach ($roles as $role) {
|
||||||
|
UserRole::create(['user_id' => $this->adminUser->id, 'role_id' => $role->id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
UserRole::where('user_id', $this->adminUser->id)->delete();
|
||||||
|
$this->adminUser->delete();
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang admin hiển thị form đăng nhập.
|
||||||
|
*/
|
||||||
|
public function test_admin_login_page_shows_form(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/admin')
|
||||||
|
->assertPresent('input[name="email"]')
|
||||||
|
->assertPresent('input[name="password"]')
|
||||||
|
->assertPresent('input[type="submit"]');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Đăng nhập admin với email/password sai phải hiện lỗi.
|
||||||
|
*/
|
||||||
|
public function test_admin_login_with_wrong_credentials_shows_error(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/admin')
|
||||||
|
->type('email', 'wrong@email.com')
|
||||||
|
->type('password', 'wrongpassword')
|
||||||
|
->press('Login')
|
||||||
|
->pause(300)
|
||||||
|
->assertPresent('.alert-danger, .error, [class*="error"]');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Đăng nhập admin với email rỗng phải hiện lỗi validation (bypass HTML5 required).
|
||||||
|
*/
|
||||||
|
public function test_admin_login_with_empty_fields_shows_validation(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/admin');
|
||||||
|
// HTML5 `required` prevents native submit, strip it to get server-side validation
|
||||||
|
$browser->script("
|
||||||
|
document.querySelectorAll('input[required]').forEach(function(el) {
|
||||||
|
el.removeAttribute('required');
|
||||||
|
});
|
||||||
|
");
|
||||||
|
$browser->press('Login')
|
||||||
|
->pause(500)
|
||||||
|
->assertPresent('.text-danger, .alert-danger');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Đăng nhập admin thành công và redirect vào trang tài khoản.
|
||||||
|
*/
|
||||||
|
public function test_admin_login_successful(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/admin/logout')
|
||||||
|
->visit('/admin')
|
||||||
|
->type('email', 'dusk_admin_test@test.local')
|
||||||
|
->type('password', 'dusk_admin_pwd_2026')
|
||||||
|
->press('Login')
|
||||||
|
->waitForLocation('/admin/account', 10)
|
||||||
|
->assertPathIs('/admin/account');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Khi đã đăng nhập admin, truy cập /admin vẫn hiển thị form login (không có redirect).
|
||||||
|
*/
|
||||||
|
public function test_authenticated_admin_visiting_login_sees_form(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin')
|
||||||
|
->assertPathIs('/admin')
|
||||||
|
->assertPresent('input[name="email"]');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truy cập trang admin khi chưa đăng nhập bị redirect về /admin login.
|
||||||
|
*/
|
||||||
|
public function test_unauthenticated_access_to_admin_account_redirects(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/admin/logout')
|
||||||
|
->visit('/admin/account')
|
||||||
|
->assertPathIs('/admin');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Đăng xuất admin thành công qua route /logout (route /admin/logout bị middleware chặn
|
||||||
|
* vì không có role admin/logout trong DB). Dùng real login thay vì loginAs()
|
||||||
|
* để tránh Dusk tự re-auth sau khi logout.
|
||||||
|
*/
|
||||||
|
public function test_admin_logout(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
// Đăng nhập thực qua form (không dùng loginAs để tránh Dusk re-auth)
|
||||||
|
$browser->visit('/admin/logout') // clear any previous session
|
||||||
|
->visit('/admin')
|
||||||
|
->type('email', $this->adminUser->email)
|
||||||
|
->type('password', 'dusk_admin_pwd_2026')
|
||||||
|
->press('Login')
|
||||||
|
->waitForLocation('/admin/account', 10)
|
||||||
|
// Thực hiện logout qua frontend /logout
|
||||||
|
->visit('/logout')
|
||||||
|
->pause(300)
|
||||||
|
// Truy cập trang cần auth -> phải bị redirect về /admin
|
||||||
|
->visit('/admin/account')
|
||||||
|
->assertPathIs('/admin');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Browser;
|
||||||
|
|
||||||
|
use App\Models\Parameter;
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\UserRole;
|
||||||
|
use Laravel\Dusk\Browser;
|
||||||
|
use Tests\DuskTestCase;
|
||||||
|
|
||||||
|
class AdminParameterTest extends DuskTestCase
|
||||||
|
{
|
||||||
|
private User $adminUser;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->adminUser = User::create([
|
||||||
|
'name' => 'DUSK PARAM ADMIN',
|
||||||
|
'email' => 'dusk_param_admin@test.local',
|
||||||
|
'phone' => '09' . rand(10000000, 99999999),
|
||||||
|
'password' => bcrypt('dusk_admin_pwd_2026'),
|
||||||
|
'status' => 1,
|
||||||
|
'address' => 'Test',
|
||||||
|
'known_sis'=> 0,
|
||||||
|
'job' => 'Admin',
|
||||||
|
'spined' => 0,
|
||||||
|
]);
|
||||||
|
$roles = Role::where('role_status', 1)->get();
|
||||||
|
foreach ($roles as $role) {
|
||||||
|
UserRole::create(['user_id' => $this->adminUser->id, 'role_id' => $role->id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
UserRole::where('user_id', $this->adminUser->id)->delete();
|
||||||
|
$this->adminUser->delete();
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang cấu hình hệ thống hiển thị.
|
||||||
|
*/
|
||||||
|
public function test_parameter_page_loads(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/parameter')
|
||||||
|
->assertPresent('form');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cấu hình game_rule hiển thị trong textarea.
|
||||||
|
*/
|
||||||
|
public function test_parameter_page_shows_game_rule_field(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/parameter')
|
||||||
|
->assertPresent('[name="game_rule"], textarea');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cập nhật thông số game_rule thành công và kiểm tra DB.
|
||||||
|
*/
|
||||||
|
public function test_update_game_rule_successfully(): void
|
||||||
|
{
|
||||||
|
// Backup giá trị hiện tại
|
||||||
|
$original = Parameter::where('parameter_key', 'game_rule')->first();
|
||||||
|
$originalValue = $original->parameter_value ?? '';
|
||||||
|
|
||||||
|
$newValue = 'Dusk Test Rule Content ' . rand(1000, 9999);
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($newValue) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/parameter')
|
||||||
|
->pause(300);
|
||||||
|
|
||||||
|
// Gán trực tiếp qua JS vì có thể là CKEditor
|
||||||
|
$browser->script("
|
||||||
|
var el = document.querySelector('[name=\"game_rule\"]');
|
||||||
|
if (el) {
|
||||||
|
el.value = '{$newValue}';
|
||||||
|
}
|
||||||
|
if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances['game_rule']) {
|
||||||
|
CKEDITOR.instances['game_rule'].setData('{$newValue}');
|
||||||
|
}
|
||||||
|
");
|
||||||
|
|
||||||
|
$browser->press('Cập nhật')
|
||||||
|
->waitFor('.alert-success', 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB: game_rule đã được cập nhật và chứa nội dung mới
|
||||||
|
$updated = Parameter::where('parameter_key', 'game_rule')->first();
|
||||||
|
$this->assertNotNull($updated, 'Tham số game_rule phải tồn tại trong DB');
|
||||||
|
$this->assertStringContainsString($newValue, $updated->parameter_value,
|
||||||
|
'Giá trị DB phải chứa nội dung vừa lưu');
|
||||||
|
|
||||||
|
// Restore giá trị gốc
|
||||||
|
$original->parameter_value = $originalValue;
|
||||||
|
$original->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang cấu hình có nút upload logo và banner.
|
||||||
|
*/
|
||||||
|
public function test_parameter_page_has_image_upload_fields(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/parameter')
|
||||||
|
->assertPresent('input[type="file"][name="logo_image"], input[name="logo_image"]');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truy cập /admin/parameter khi chưa đăng nhập bị redirect.
|
||||||
|
*/
|
||||||
|
public function test_parameter_page_requires_auth(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/admin/logout')
|
||||||
|
->visit('/admin/parameter')
|
||||||
|
->assertPathIs('/admin');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Browser;
|
||||||
|
|
||||||
|
use App\Models\Prize;
|
||||||
|
use App\Models\PrizeUser;
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\UserRole;
|
||||||
|
use Laravel\Dusk\Browser;
|
||||||
|
use Tests\DuskTestCase;
|
||||||
|
|
||||||
|
class AdminPrizeTest extends DuskTestCase
|
||||||
|
{
|
||||||
|
private User $adminUser;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->adminUser = User::create([
|
||||||
|
'name' => 'DUSK PRIZE ADMIN',
|
||||||
|
'email' => 'dusk_prize_admin@test.local',
|
||||||
|
'phone' => '09' . rand(10000000, 99999999),
|
||||||
|
'password' => bcrypt('dusk_admin_pwd_2026'),
|
||||||
|
'status' => 1,
|
||||||
|
'address' => 'Test',
|
||||||
|
'known_sis'=> 0,
|
||||||
|
'job' => 'Admin',
|
||||||
|
'spined' => 0,
|
||||||
|
]);
|
||||||
|
$roles = Role::where('role_status', 1)->get();
|
||||||
|
foreach ($roles as $role) {
|
||||||
|
UserRole::create(['user_id' => $this->adminUser->id, 'role_id' => $role->id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
UserRole::where('user_id', $this->adminUser->id)->delete();
|
||||||
|
$this->adminUser->delete();
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang danh sách giải thưởng hiển thị đúng.
|
||||||
|
*/
|
||||||
|
public function test_prize_list_page_loads(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prizes')
|
||||||
|
->assertPresent('table, .table');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang thêm mới giải thưởng hiển thị form.
|
||||||
|
*/
|
||||||
|
public function test_add_prize_form_loads(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prize/update?id=0')
|
||||||
|
->assertPresent('input[name="prize_name"]')
|
||||||
|
->assertPresent('input[name="prize_order"]')
|
||||||
|
->assertPresent('select[name="prize_status"]');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit form thêm giải thưởng rỗng phải hiện lỗi.
|
||||||
|
*/
|
||||||
|
public function test_add_prize_with_empty_name_shows_error(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prize/update?id=0');
|
||||||
|
// Strip HTML5 required nếu có, rồi submit để Server-side validation chạy
|
||||||
|
$browser->script("document.querySelectorAll('input[required]').forEach(function(el){el.removeAttribute('required');})");
|
||||||
|
$browser->press('Thêm mới')
|
||||||
|
->pause(500)
|
||||||
|
->assertPresent('.text-danger, .alert-danger, [class*="error"]');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thêm mới giải thưởng thành công và kiểm tra DB.
|
||||||
|
*/
|
||||||
|
public function test_create_prize_successfully(): void
|
||||||
|
{
|
||||||
|
$prizeName = 'Dusk Test Prize ' . rand(1000, 9999);
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($prizeName) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prize/update?id=0')
|
||||||
|
->pause(500)
|
||||||
|
->type('prize_name', $prizeName)
|
||||||
|
->type('prize_number', '100')
|
||||||
|
->type('prize_order', '99')
|
||||||
|
->select('prize_status', '1')
|
||||||
|
->press('Thêm mới')
|
||||||
|
->waitFor('.alert-success', 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB
|
||||||
|
$prize = Prize::where('prize_name', $prizeName)->first();
|
||||||
|
$this->assertNotNull($prize, 'Giải thưởng phải được tạo trong DB');
|
||||||
|
$this->assertEquals(1, $prize->prize_status);
|
||||||
|
$this->assertEquals(100, $prize->prize_number);
|
||||||
|
$this->assertEquals(99, $prize->prize_order);
|
||||||
|
|
||||||
|
// Dọn dẹp
|
||||||
|
$prize->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cập nhật tên giải thưởng thành công.
|
||||||
|
*/
|
||||||
|
public function test_update_prize_successfully(): void
|
||||||
|
{
|
||||||
|
// Tạo giải thưởng để cập nhật
|
||||||
|
$prize = Prize::create([
|
||||||
|
'prize_name' => 'Dusk Prize To Update',
|
||||||
|
'prize_status' => 1,
|
||||||
|
'prize_number' => 10,
|
||||||
|
'prize_order' => 88,
|
||||||
|
'user_id' => $this->adminUser->id,
|
||||||
|
]);
|
||||||
|
$newName = 'Dusk Prize Updated ' . rand(1000, 9999);
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($prize, $newName) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prize/update?id=' . $prize->id)
|
||||||
|
->clear('prize_name')
|
||||||
|
->type('prize_name', $newName)
|
||||||
|
->press('Cập nhật')
|
||||||
|
->waitFor('.alert-success', 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB
|
||||||
|
$updated = Prize::find($prize->id);
|
||||||
|
$this->assertEquals($newName, $updated->prize_name);
|
||||||
|
|
||||||
|
$prize->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Xóa giải thưởng thành công.
|
||||||
|
*/
|
||||||
|
public function test_delete_prize_successfully(): void
|
||||||
|
{
|
||||||
|
$prize = Prize::create([
|
||||||
|
'prize_name' => 'Dusk Prize To Delete',
|
||||||
|
'prize_status' => 1,
|
||||||
|
'prize_number' => 5,
|
||||||
|
'prize_order' => 77,
|
||||||
|
'user_id' => $this->adminUser->id,
|
||||||
|
]);
|
||||||
|
$prizeId = $prize->id;
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($prizeId) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prizes')
|
||||||
|
->visit('/admin/prize/delete/' . $prizeId)
|
||||||
|
->pause(1000); // redirect()->back() sau khi xóa
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB: giải thưởng đã bị xóa
|
||||||
|
$this->assertNull(Prize::find($prizeId), 'Giải thưởng phải bị xóa khỏi DB');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang danh sách người trúng giải hiển thị.
|
||||||
|
*/
|
||||||
|
public function test_prize_winner_list_loads(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prizeuser')
|
||||||
|
->assertPresent('table, .table');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang danh sách người trúng giải có ô lọc ngày.
|
||||||
|
*/
|
||||||
|
public function test_prize_winner_list_has_date_filter(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prizeuser')
|
||||||
|
->assertPresent('#fromDateTime, #toDateTime');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link export Excel hoạt động.
|
||||||
|
*/
|
||||||
|
public function test_prize_winner_export_link_present(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/prizeuser')
|
||||||
|
->assertPresent('#excel_export');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Browser;
|
||||||
|
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\UserRole;
|
||||||
|
use Laravel\Dusk\Browser;
|
||||||
|
use Tests\DuskTestCase;
|
||||||
|
|
||||||
|
class AdminUserTest extends DuskTestCase
|
||||||
|
{
|
||||||
|
private User $adminUser;
|
||||||
|
private User $targetUser;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->adminUser = User::create([
|
||||||
|
'name' => 'DUSK USER ADMIN',
|
||||||
|
'email' => 'dusk_user_admin@test.local',
|
||||||
|
'phone' => '09' . rand(10000000, 99999999),
|
||||||
|
'password' => bcrypt('dusk_admin_pwd_2026'),
|
||||||
|
'status' => 1,
|
||||||
|
'address' => 'Test',
|
||||||
|
'known_sis'=> 0,
|
||||||
|
'job' => 'Admin',
|
||||||
|
'spined' => 0,
|
||||||
|
]);
|
||||||
|
$roles = Role::where('role_status', 1)->get();
|
||||||
|
foreach ($roles as $role) {
|
||||||
|
UserRole::create(['user_id' => $this->adminUser->id, 'role_id' => $role->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tạo user mục tiêu để test cập nhật
|
||||||
|
$targetPhone = '09' . rand(10000000, 99999999);
|
||||||
|
$this->targetUser = User::create([
|
||||||
|
'name' => 'DUSK TARGET USER',
|
||||||
|
'email' => $targetPhone . '@phone.local',
|
||||||
|
'phone' => $targetPhone,
|
||||||
|
'password' => bcrypt('test123'),
|
||||||
|
'status' => 1,
|
||||||
|
'address' => 'Cần Thơ',
|
||||||
|
'known_sis'=> 1,
|
||||||
|
'job' => 'Sinh viên',
|
||||||
|
'spined' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
UserRole::where('user_id', $this->adminUser->id)->delete();
|
||||||
|
$this->adminUser->delete();
|
||||||
|
$this->targetUser->delete();
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang danh sách user hiển thị.
|
||||||
|
*/
|
||||||
|
public function test_user_list_page_loads(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/users')
|
||||||
|
->assertPresent('table, .table');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tên user mới tạo xuất hiện trong danh sách.
|
||||||
|
*/
|
||||||
|
public function test_user_list_shows_target_user(): void
|
||||||
|
{
|
||||||
|
// DB assertion: target user có trong query của controller (status != '')
|
||||||
|
$userInList = User::where('id', $this->targetUser->id)
|
||||||
|
->where('status', '!=', '')
|
||||||
|
->first();
|
||||||
|
$this->assertNotNull($userInList, 'Target user must be included in admin user list query');
|
||||||
|
$this->assertEquals($this->targetUser->email, $userInList->email);
|
||||||
|
|
||||||
|
// Browser: trang list load được với table
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/users')
|
||||||
|
->waitFor('#table', 5)
|
||||||
|
->assertPresent('#table');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trang form cập nhật user hiển thị đúng thông tin.
|
||||||
|
*/
|
||||||
|
public function test_edit_user_form_shows_existing_data(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/users/update?id=' . $this->targetUser->id)
|
||||||
|
->assertInputValue('name', $this->targetUser->name)
|
||||||
|
->assertInputValue('email', $this->targetUser->email);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cập nhật tên user thành công và kiểm tra DB.
|
||||||
|
*/
|
||||||
|
public function test_update_user_name_successfully(): void
|
||||||
|
{
|
||||||
|
$newName = 'Updated Dusk Name';
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($newName) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/users/update?id=' . $this->targetUser->id)
|
||||||
|
->clear('name')
|
||||||
|
->type('name', $newName)
|
||||||
|
->press('Cập nhật')
|
||||||
|
->waitFor('.alert-success', 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB: admin không chuyển uppercase
|
||||||
|
$updated = User::find($this->targetUser->id);
|
||||||
|
$this->assertEquals($newName, $updated->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Xem trang tài khoản admin.
|
||||||
|
*/
|
||||||
|
public function test_admin_account_page_shows_user_info(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->loginAs($this->adminUser)
|
||||||
|
->visit('/admin/account')
|
||||||
|
// Email là input field (readonly), dùng assertInputValue
|
||||||
|
->assertInputValue('email', $this->adminUser->email);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Tests\Browser;
|
namespace Tests\Browser;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Laravel\Dusk\Browser;
|
use Laravel\Dusk\Browser;
|
||||||
use Tests\DuskTestCase;
|
use Tests\DuskTestCase;
|
||||||
|
|
||||||
@@ -13,7 +14,8 @@ class CreateAccountTest extends DuskTestCase
|
|||||||
public function test_login_page_shows_form(): void
|
public function test_login_page_shows_form(): void
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->visit('/login')
|
$browser->visit('/logout') // đảm bảo trạng thái guest
|
||||||
|
->visit('/login')
|
||||||
->assertSee('Nhập thông tin cá nhân')
|
->assertSee('Nhập thông tin cá nhân')
|
||||||
->assertPresent('input[name="name"]')
|
->assertPresent('input[name="name"]')
|
||||||
->assertPresent('input[name="phone"]')
|
->assertPresent('input[name="phone"]')
|
||||||
@@ -28,8 +30,10 @@ class CreateAccountTest extends DuskTestCase
|
|||||||
public function test_empty_form_shows_validation_errors(): void
|
public function test_empty_form_shows_validation_errors(): void
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->visit('/login')
|
$browser->visit('/logout') // đảm bảo trạng thái guest
|
||||||
|
->visit('/login')
|
||||||
->press('Gửi thông tin')
|
->press('Gửi thông tin')
|
||||||
|
->waitFor('.text-danger', 5)
|
||||||
->assertPresent('.text-danger');
|
->assertPresent('.text-danger');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -71,6 +75,18 @@ class CreateAccountTest extends DuskTestCase
|
|||||||
->waitForText('thành công', 10)
|
->waitForText('thành công', 10)
|
||||||
->assertPathIs('/game');
|
->assertPathIs('/game');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB: user đã được tạo đúng
|
||||||
|
$user = User::where('phone', $uniquePhone)->first();
|
||||||
|
$this->assertNotNull($user, 'User phải được tạo trong DB');
|
||||||
|
$this->assertEquals('TEST DUSK USER', $user->name, 'Tên phải được uppercase');
|
||||||
|
$this->assertEquals('Cần Thơ', $user->address);
|
||||||
|
$this->assertEquals(1, $user->known_sis);
|
||||||
|
$this->assertEquals('Kỹ sư', $user->job);
|
||||||
|
$this->assertEquals($uniquePhone . '@phone.local', $user->email);
|
||||||
|
|
||||||
|
// Dọn dẹp
|
||||||
|
$user->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,5 +115,14 @@ class CreateAccountTest extends DuskTestCase
|
|||||||
$browser->visit('/login')
|
$browser->visit('/login')
|
||||||
->assertInputValue('phone', $uniquePhone);
|
->assertInputValue('phone', $uniquePhone);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Kiểm tra DB: user tồn tại và spined = 0 (chưa quay)
|
||||||
|
$user = User::where('phone', $uniquePhone)->first();
|
||||||
|
$this->assertNotNull($user, 'User phải tồn tại trong DB');
|
||||||
|
$this->assertEquals(0, $user->spined, 'Người dùng mới chưa được quay');
|
||||||
|
$this->assertEquals(1, $user->status, 'Tài khoản phải ở trạng thái active');
|
||||||
|
|
||||||
|
// Dọn dẹp
|
||||||
|
$user->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ class ExampleTest extends DuskTestCase
|
|||||||
public function test_basic_example(): void
|
public function test_basic_example(): void
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
|
// Cả / và /game đều render view game (không redirect)
|
||||||
$browser->visit('/')
|
$browser->visit('/')
|
||||||
->assertPathIs('/game');
|
->assertPresent('#wheelCanvas');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+162
-10
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
namespace Tests\Browser;
|
namespace Tests\Browser;
|
||||||
|
|
||||||
|
use App\Models\PrizeUser;
|
||||||
|
use App\Models\User;
|
||||||
use Laravel\Dusk\Browser;
|
use Laravel\Dusk\Browser;
|
||||||
use Tests\DuskTestCase;
|
use Tests\DuskTestCase;
|
||||||
|
|
||||||
class GamePageTest extends DuskTestCase
|
class GamePageTest extends DuskTestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Trang game hiển thị đúng khi chưa đăng nhập.
|
* Trang game tải được khi chưa đăng nhập.
|
||||||
*/
|
*/
|
||||||
public function test_game_page_loads_when_guest(): void
|
public function test_game_page_loads_when_guest(): void
|
||||||
{
|
{
|
||||||
@@ -19,35 +21,185 @@ class GamePageTest extends DuskTestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trang chủ (/) redirect về game.
|
* URL gốc (/) redirect về /game.
|
||||||
*/
|
*/
|
||||||
public function test_root_redirects_to_game(): void
|
public function test_root_redirects_to_game(): void
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->visit('/')
|
$browser->visit('/')
|
||||||
->assertPathIs('/game');
|
->assertPresent('#wheelCanvas'); // / và /game cùng render game
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trang game hiển thị vòng quay may mắn.
|
* Vòng quay (canvas) hiển thị trên trang game.
|
||||||
*/
|
*/
|
||||||
public function test_game_page_shows_lucky_wheel(): void
|
public function test_game_page_shows_lucky_wheel_canvas(): void
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->visit('/game')
|
$browser->visit('/game')
|
||||||
->assertPresent('#wheel, canvas, .wheel, [id*="wheel"], [class*="wheel"]');
|
->assertPresent('#wheelCanvas');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Popup luật chơi có thể tải được.
|
* Nút quay hiển thị trên trang game.
|
||||||
*/
|
*/
|
||||||
public function test_game_rule_endpoint_returns_content(): void
|
public function test_spin_button_is_present(): void
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->visit('/game/rule')
|
$browser->visit('/game')
|
||||||
->assertSee('luật');
|
->assertPresent('#spinButton');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Banner "Thể lệ tham dự" hiển thị.
|
||||||
|
*/
|
||||||
|
public function test_game_rule_ribbon_is_present(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/game')
|
||||||
|
->assertPresent('#game_rule');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Khi guest vào /game, hiển thị thông báo yêu cầu đăng nhập.
|
||||||
|
*/
|
||||||
|
public function test_guest_sees_login_prompt_on_game(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/logout')
|
||||||
|
->visit('/game')
|
||||||
|
->assertPresent('.alert_wheel_spin')
|
||||||
|
->assertPresent('a[href*="login"]');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Khi đã đăng nhập và chưa quay, nút quay ở trạng thái sẵn sàng.
|
||||||
|
*/
|
||||||
|
public function test_logged_in_user_can_see_spin_button_enabled(): void
|
||||||
|
{
|
||||||
|
$phone = '09' . rand(10000000, 99999999);
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($phone) {
|
||||||
|
$browser->visit('/logout')
|
||||||
|
->visit('/login')
|
||||||
|
->pause(300)
|
||||||
|
->type('name', 'Spin Test User')
|
||||||
|
->type('phone', $phone)
|
||||||
|
->type('address', 'TP.HCM')
|
||||||
|
->radio('known_sis', '1')
|
||||||
|
->type('job', 'Kỹ sư')
|
||||||
|
->press('Gửi thông tin')
|
||||||
|
->waitForText('thành công', 10)
|
||||||
|
->assertPathIs('/game')
|
||||||
|
->assertAttribute('#spinButton', 'data-check', 'true');
|
||||||
|
});
|
||||||
|
|
||||||
|
User::where('phone', $phone)->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Khi đã quay rồi (spined=1), nút quay bị khóa.
|
||||||
|
*/
|
||||||
|
public function test_already_spined_user_sees_spin_button_disabled(): void
|
||||||
|
{
|
||||||
|
$phone = '09' . rand(10000000, 99999999);
|
||||||
|
|
||||||
|
// Tạo user với spined=1
|
||||||
|
$user = User::create([
|
||||||
|
'name' => 'SPINED USER',
|
||||||
|
'phone' => $phone,
|
||||||
|
'email' => $phone . '@phone.local',
|
||||||
|
'password' => bcrypt('test123'),
|
||||||
|
'address' => 'Cần Thơ',
|
||||||
|
'known_sis' => 1,
|
||||||
|
'job' => 'Kỹ sư',
|
||||||
|
'status' => 1,
|
||||||
|
'spined' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($user) {
|
||||||
|
$browser->visit('/logout')
|
||||||
|
->loginAs($user)
|
||||||
|
->visit('/game')
|
||||||
|
->assertAttribute('#spinButton', 'data-check', 'false');
|
||||||
|
});
|
||||||
|
|
||||||
|
$user->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup luật chơi tải nội dung.
|
||||||
|
*/
|
||||||
|
public function test_game_rule_popup_loads(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/logout')
|
||||||
|
->visit('/game')
|
||||||
|
->pause(500);
|
||||||
|
|
||||||
|
// Dismiss alerts che button rồi click bằng JS
|
||||||
|
$browser->script("document.querySelectorAll('.alert').forEach(el => el.remove())");
|
||||||
|
$browser->script("document.getElementById('game_rule').click()");
|
||||||
|
|
||||||
|
$browser->pause(800)
|
||||||
|
->assertPresent('#gamerule');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API /game/rule trả về nội dung HTML.
|
||||||
|
*/
|
||||||
|
public function test_game_rule_api_returns_html(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
// /game/rule được gọi qua AJAX, trả về fragment HTML không có <body>
|
||||||
|
$browser->visit('/game/rule')
|
||||||
|
->assertSourceHas('class='); // snippet HTML luon co class=
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API /checkspined trả lỗi khi chưa đăng nhập.
|
||||||
|
*/
|
||||||
|
public function test_checkspined_returns_error_for_guest(): void
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser->visit('/logout')
|
||||||
|
->visit('/checkspined')
|
||||||
|
->assertSee('error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API /checkspined trả spined=0 khi đăng nhập user mới.
|
||||||
|
*/
|
||||||
|
public function test_checkspined_returns_zero_for_new_user(): void
|
||||||
|
{
|
||||||
|
$phone = '09' . rand(10000000, 99999999);
|
||||||
|
$user = User::create([
|
||||||
|
'name' => 'CHECK SPINED USER',
|
||||||
|
'phone' => $phone,
|
||||||
|
'email' => $phone . '@phone.local',
|
||||||
|
'password' => bcrypt('test123'),
|
||||||
|
'address' => 'Cần Thơ',
|
||||||
|
'known_sis' => 0,
|
||||||
|
'job' => 'Sinh viên',
|
||||||
|
'status' => 1,
|
||||||
|
'spined' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->browse(function (Browser $browser) use ($user) {
|
||||||
|
$browser->visit('/logout')
|
||||||
|
->loginAs($user)
|
||||||
|
->visit('/checkspined')
|
||||||
|
->assertSee('"spined":0');
|
||||||
|
});
|
||||||
|
|
||||||
|
$user->delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user