import io import pytest from django.core.management import call_command from playwright.sync_api import expect from apps.accounts.models import User from apps.customers.models import CustomerProfile from apps.staff.models import OrganizationalUnit, StaffProfile from apps.tickets.models import Ticket, TicketCategory from tests.e2e.pages.auth import AuthenticationPage from tests.e2e.pages.tickets import PublicTicketCreatePage pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] PASSWORD = "E2e-public-workflow-2026" def image_file(name, image_format, color): pillow = pytest.importorskip("PIL.Image") stream = io.BytesIO() pillow.new("RGB", (8, 8), color).save(stream, format=image_format) return { "name": name, "mimeType": "image/jpeg" if image_format == "JPEG" else "image/png", "buffer": stream.getvalue(), } def pdf_file(name, marker="one"): return { "name": name, "mimeType": "application/pdf", "buffer": ( "%PDF-1.4\n1 0 obj<>endobj\n" "2 0 obj<>endobj\n" f"% {marker}\n%%EOF\n" ).encode(), } @pytest.fixture def public_workflow(settings, tmp_path): settings.MEDIA_ROOT = tmp_path call_command("bootstrap") customer = User.objects.create_user( username="e2e-public-workflow", mobile="09120008301", password=PASSWORD, account_type=User.AccountType.CUSTOMER, ) CustomerProfile.objects.create(user=customer, first_name="مشتری", last_name="مرورگر") units = { slug: OrganizationalUnit.objects.get(slug=slug) for slug in ("sales", "warehouse-loading", "finance") } categories = { "sales_general": TicketCategory.objects.get(slug="sales-general"), "sales_secondary": TicketCategory.objects.create( unit=units["sales"], title="شرایط فروش E2E", slug="sales-secondary-e2e", is_active=True, show_in_customer_form=True, ), "warehouse": TicketCategory.objects.get(slug="warehouse-loading-general"), "finance": TicketCategory.objects.get(slug="finance-general"), } return customer, units, categories def open_form(page, live_server, customer): AuthenticationPage(page, live_server.url).login_customer(customer, PASSWORD) form = PublicTicketCreatePage(page, live_server.url) form.open() return form def assert_ticket_record(subject, *, unit, category, expert_name, attachment_names=()): ticket = Ticket.objects.select_related("selected_expert__staff_profile").get(subject=subject) assert ticket.owning_unit == unit assert ticket.assigned_unit == unit assert ticket.category == category actual_expert = ticket.selected_expert.staff_profile.display_name if ticket.selected_expert_id else "صف عمومی" assert actual_expert == expert_name attachments = ticket.ticket_messages.get().attachments.order_by("original_name") assert set(attachments.values_list("original_name", flat=True)) == set(attachment_names) def assert_final_ticket_ui(page, *, category, expert_name, attachment_names=()): expect(page.locator("#ticket-info")).to_contain_text(category.title) expect(page.locator("#ticket-info")).to_contain_text(expert_name) for name in attachment_names: expect(page.locator("[data-conversation]")).to_contain_text(name) def test_e2e_07_sales_general_queue_creates_verifiable_ticket(live_server, browser_page, public_workflow): customer, units, categories = public_workflow subject = "ثبت واقعی در صف عمومی فروش E2E" with browser_page() as page: form = open_form(page, live_server, customer) form.select_unit(units["sales"].pk) form.select_category(categories["sales_general"].pk) form.select_general_queue() form.submit(subject=subject, body="شرح کامل تیکت بدون پیوست") assert_final_ticket_ui(page, category=categories["sales_general"], expert_name="صف عمومی") assert_ticket_record( subject, unit=units["sales"], category=categories["sales_general"], expert_name="صف عمومی" ) def test_e2e_08_repeated_staff_changes_store_only_last_choice(live_server, browser_page, public_workflow): customer, units, categories = public_workflow subject = "آخرین کارشناس انتخابی E2E" with browser_page() as page: form = open_form(page, live_server, customer) form.select_unit(units["sales"].pk) form.select_category(categories["sales_general"].pk) for name in ("کیمیا غفاری", "زهرا جمالی", "مریم فتوت", "زهرا جمالی"): form.select_expert(name) form.submit(subject=subject, body="تغییر چندباره کارشناس") assert_final_ticket_ui(page, category=categories["sales_general"], expert_name="زهرا جمالی") assert_ticket_record( subject, unit=units["sales"], category=categories["sales_general"], expert_name="زهرا جمالی" ) def test_e2e_subject_stays_empty_for_manual_and_search_routing_until_user_types( live_server, browser_page, public_workflow ): customer, units, categories = public_workflow subject = "موضوع کاملاً دستی E2E" with browser_page() as page: form = open_form(page, live_server, customer) subject_input = page.locator("[data-ticket-subject]") unit_box = form.unit.bounding_box() category_box = form.category.bounding_box() assert unit_box and category_box assert abs(unit_box["y"] - category_box["y"]) < 2 assert unit_box["x"] + unit_box["width"] <= category_box["x"] or category_box["x"] + category_box["width"] <= unit_box["x"] expect(subject_input).to_have_value("") expect(subject_input).to_have_attribute("required", "") form.select_unit(units["finance"].pk) form.select_category(categories["finance"].pk) expect(subject_input).to_have_value("") form.select_search_result("عمومی فروش", "عمومی فروش") expect(form.unit).to_have_value(str(units["sales"].pk)) expect(subject_input).to_have_value("") subject_input.fill(subject) form.select_unit(units["finance"].pk) expect(subject_input).to_have_value(subject) form.select_unit(units["sales"].pk) form.select_category(categories["sales_general"].pk) form.select_general_queue() form.submit(subject=subject, body="موضوع فقط توسط کاربر وارد شده است") assert_final_ticket_ui(page, category=categories["sales_general"], expert_name="صف عمومی") assert_ticket_record( subject, unit=units["sales"], category=categories["sales_general"], expert_name="صف عمومی" ) def test_e2e_experts_follow_configured_numeric_order(live_server, browser_page, public_workflow): customer, units, _ = public_workflow configured = { "زهرا جمالی": 10, "مریم فتوت": 20, "کیمیا غفاری": 30, "مرتضی سخائی فرد": 40, } for name, order in configured.items(): StaffProfile.objects.filter(primary_unit=units["sales"], display_name=name).update(sort_order=order) with browser_page() as page: form = open_form(page, live_server, customer) form.select_unit(units["sales"].pk) names = form.expert_picker.locator( "label.expert-card:not([data-general-queue]) strong" ).all_text_contents() assert names == ["زهرا جمالی", "مریم فتوت", "کیمیا غفاری", "مرتضی سخائی فرد"] @pytest.mark.parametrize( "suffix,images,documents,expected", [ ("jpg", [image_file("photo.jpg", "JPEG", "red")], [], ["photo.jpg"]), ("pdf", [], [pdf_file("document.pdf")], ["document.pdf"]), ( "mixed", [image_file("first.jpg", "JPEG", "blue"), image_file("second.png", "PNG", "green")], [pdf_file("combined.pdf")], ["first.jpg", "second.png", "combined.pdf"], ), ( "multi-file", [], [pdf_file("first.pdf", "first"), pdf_file("second.pdf", "second")], ["first.pdf", "second.pdf"], ), ], ) def test_e2e_09_10_11_attachment_variants_are_saved_and_rendered( live_server, browser_page, public_workflow, suffix, images, documents, expected ): customer, units, categories = public_workflow subject = f"پیوست واقعی {suffix} E2E" with browser_page() as page: form = open_form(page, live_server, customer) form.select_unit(units["sales"].pk) form.select_category(categories["sales_general"].pk) form.select_general_queue() if images: form.upload_gallery(images) if documents: form.upload_documents(documents, total_count=len(images) + len(documents)) form.submit(subject=subject, body="بررسی ذخیره و نمایش تمام پیوست‌ها") assert_final_ticket_ui( page, category=categories["sales_general"], expert_name="صف عمومی", attachment_names=expected ) assert_ticket_record( subject, unit=units["sales"], category=categories["sales_general"], expert_name="صف عمومی", attachment_names=expected, ) def test_e2e_11_removed_attachment_is_not_submitted(live_server, browser_page, public_workflow): customer, units, categories = public_workflow subject = "حذف پیوست پیش از ثبت E2E" keep = image_file("keep.png", "PNG", "yellow") remove = pdf_file("remove.pdf") with browser_page() as page: form = open_form(page, live_server, customer) form.select_unit(units["sales"].pk) form.select_category(categories["sales_general"].pk) form.upload_gallery([keep]) form.upload_documents([remove], total_count=2) form.remove_attachment("remove.pdf") form.submit(subject=subject, body="فقط تصویر باید ذخیره شود") assert_final_ticket_ui( page, category=categories["sales_general"], expert_name="صف عمومی", attachment_names=["keep.png"] ) assert_ticket_record( subject, unit=units["sales"], category=categories["sales_general"], expert_name="صف عمومی", attachment_names=["keep.png"], ) def test_e2e_12_sequential_unit_staff_category_and_attachment_state(live_server, browser_page, public_workflow): customer, units, categories = public_workflow subject = "مدیریت وضعیت متوالی فرم E2E" image_one = image_file("state-one.jpg", "JPEG", "purple") image_two = image_file("state-two.png", "PNG", "orange") removed_pdf = pdf_file("state-remove.pdf") with browser_page() as page: form = open_form(page, live_server, customer) form.select_unit(units["sales"].pk) form.select_expert("کیمیا غفاری") form.select_category(categories["sales_secondary"].pk) form.upload_gallery([image_one]) form.upload_documents([removed_pdf], total_count=2) form.select_unit(units["finance"].pk) expect(form.expert_picker).not_to_contain_text("کیمیا غفاری") expect(form.queue).to_contain_text("صف عمومی مالی / حسابداری") form.select_category(categories["finance"].pk) form.select_unit(units["sales"].pk) form.select_category(categories["sales_general"].pk) form.select_expert("کیمیا غفاری") form.select_expert("زهرا جمالی") form.select_expert("کیمیا غفاری") form.remove_attachment("state-remove.pdf") form.upload_gallery([image_two], total_count=2) expect(form.file_preview.locator(".file-chip")).to_have_count(2) form.submit(subject=subject, body="فرم پس از تغییرات متوالی سالم ماند") assert_final_ticket_ui( page, category=categories["sales_general"], expert_name="کیمیا غفاری", attachment_names=["state-one.jpg", "state-two.png"], ) assert_ticket_record( subject, unit=units["sales"], category=categories["sales_general"], expert_name="کیمیا غفاری", attachment_names=["state-one.jpg", "state-two.png"], )