181 lines
13 KiB
Python
181 lines
13 KiB
Python
import base64
|
||
|
||
import pytest
|
||
from django.contrib.auth.models import Permission
|
||
from django.urls import reverse
|
||
from django.utils import timezone
|
||
from playwright.sync_api import expect
|
||
|
||
from apps.accounts.models import TicketMagicLink, User
|
||
from apps.customers.models import ContactGroup, CustomerProfile
|
||
from apps.core.models import PageTitle, SiteBranding
|
||
from apps.notifications.models import NotificationJob, SMSTemplate
|
||
from apps.staff.models import OrganizationalUnit, StaffProfile
|
||
from apps.tickets.models import TicketCategory
|
||
from apps.tickets.services import create_ticket, dispatch_group_tickets
|
||
from tests.e2e.pages.auth import AuthenticationPage
|
||
|
||
|
||
pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)]
|
||
PASSWORD = "E2e-safe-password-2026"
|
||
|
||
|
||
def test_staff_operations_pages_are_authorized_and_visually_styled(live_server, browser_page):
|
||
branding = SiteBranding.load()
|
||
branding.staff_brand_title = "مرکز عملیات E2E"
|
||
branding.staff_brand_subtitle = "ارتباطات یکپارچه E2E"
|
||
branding.login_intro_eyebrow = "معرفی ورود E2E"
|
||
branding.developer_credit_text = "توسعه E2E | 09120000000"
|
||
branding.staff_sidebar_credit_text = "امضای مستقل sidebar E2E"
|
||
branding.save()
|
||
PageTitle.objects.update_or_create(route_name="staff_tickets:dispatch_create", defaults={"title": "ردیابی E2E"})
|
||
SMSTemplate.objects.update_or_create(unit=None, event_type=SMSTemplate.Event.STAFF_REPLY, defaults={"title": "الگوی E2E", "body": "{ticket_number}: {magic_link}"})
|
||
unit = OrganizationalUnit.objects.create(name="عملیات E2E", slug="operations-e2e")
|
||
permissions = {
|
||
"customers": {"view_customerprofile", "add_customerprofile", "change_customerprofile", "view_contactgroup", "add_contactgroup", "change_contactgroup"},
|
||
"tickets": {"view_ticket", "add_ticket", "dispatch_group_tickets"},
|
||
"notifications": {"view_smstemplate", "add_smstemplate", "change_smstemplate"},
|
||
"accounts": {"view_ticketmagiclink"},
|
||
}
|
||
query = None
|
||
from django.db.models import Q
|
||
for app_label, codenames in permissions.items():
|
||
part = Q(content_type__app_label=app_label, codename__in=codenames)
|
||
query = part if query is None else query | part
|
||
unit.permissions.add(*Permission.objects.filter(query))
|
||
staff = User.objects.create_user(username="e2e-operations", password=PASSWORD, account_type=User.AccountType.STAFF)
|
||
StaffProfile.objects.create(user=staff, display_name="کارشناس عملیات", primary_unit=unit)
|
||
customer = User.objects.create_user(username="e2e-carrier", mobile="09120009121", account_type=User.AccountType.CUSTOMER)
|
||
profile = CustomerProfile.objects.create(user=customer, first_name="باربری", stakeholder_type=CustomerProfile.StakeholderType.CARRIER)
|
||
group = ContactGroup.objects.create(unit=unit, name="باربریها")
|
||
secondary_group = ContactGroup.objects.create(unit=unit, name="مخاطبان منتخب")
|
||
profile.groups.add(group)
|
||
category = TicketCategory.objects.create(title="استعلام E2E", slug="operations-e2e")
|
||
ticket = create_ticket(customer=customer, created_by=staff, unit=unit, category=category, subject="استعلام آزمایشی", body="قیمت را اعلام کنید")
|
||
TicketMagicLink.issue_with_record(ticket, created_by=staff)
|
||
|
||
with browser_page() as page:
|
||
page.goto(f"{live_server.url}{reverse('accounts:login')}", wait_until="domcontentloaded")
|
||
expect(page.locator(".auth-brand strong")).to_have_count(0)
|
||
expect(page.locator(".auth-intro .eyebrow")).to_have_text("معرفی ورود E2E")
|
||
expect(page.locator(".auth-footer")).to_contain_text("توسعه E2E")
|
||
AuthenticationPage(page, live_server.url).login_staff(staff, PASSWORD)
|
||
for route, heading in (
|
||
("staff_tickets:contacts", "مخاطبان و مشتریان"),
|
||
("staff_tickets:groups", "گروههای مخاطبان"),
|
||
("staff_tickets:contact_create", "ثبت مخاطب بیرونی"),
|
||
("staff_tickets:sms_templates", "الگوهای پیامک واحدها"),
|
||
("staff_tickets:dispatch_create", "ارسال گروهی ایزوله"),
|
||
("staff_tickets:magic_audit", "ممیزی لینکهای جادویی"),
|
||
):
|
||
page.goto(f"{live_server.url}{reverse(route)}", wait_until="domcontentloaded")
|
||
expect(page.get_by_role("heading", name=heading, level=1)).to_be_visible()
|
||
expect(page.locator('link[href*="operations.css"]')).to_have_count(1)
|
||
assert page.evaluate("getComputedStyle(document.querySelector('.operations-page')).display") == "grid"
|
||
assert page.evaluate("document.documentElement.scrollWidth <= document.documentElement.clientWidth + 1")
|
||
|
||
page.goto(f"{live_server.url}{reverse('staff_tickets:dispatch_create')}", wait_until="domcontentloaded")
|
||
expect(page).to_have_title("ردیابی E2E")
|
||
expect(page.locator(".staff-brand-copy b")).to_have_text("مرکز عملیات E2E")
|
||
expect(page.locator(".staff-developer-credit")).to_contain_text("امضای مستقل sidebar E2E")
|
||
expect(page.get_by_role("searchbox", name="جستجو")).to_be_visible()
|
||
expect(page.locator('link[href*="dispatches.css"]')).to_have_count(1)
|
||
expect(page.get_by_text("افزودن عکس از گالری")).to_be_visible()
|
||
expect(page.locator('label[for="dispatch-file-input"]')).to_be_visible()
|
||
tiny_png = base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=")
|
||
page.locator("#dispatch-gallery-input").set_input_files([
|
||
{"name":"dispatch-first.png","mimeType":"image/png","buffer":tiny_png},
|
||
{"name":"dispatch-second.png","mimeType":"image/png","buffer":tiny_png},
|
||
])
|
||
expect(page.locator(".dispatch-attachment-picker [data-file-preview] .file-chip")).to_have_count(2)
|
||
expect(page.locator(".dispatch-attachment-picker [data-file-preview] img")).to_have_count(2)
|
||
page.locator("#dispatch-file-input").set_input_files({"name":"dispatch-document.pdf","mimeType":"application/pdf","buffer":b"%PDF-1.4\n%%EOF"})
|
||
expect(page.locator(".dispatch-attachment-picker [data-file-preview] .file-chip")).to_have_count(3)
|
||
page.locator(".dispatch-attachment-picker [data-file-preview] .file-chip button").first.click()
|
||
expect(page.locator(".dispatch-attachment-picker [data-file-preview] .file-chip")).to_have_count(2)
|
||
assert page.locator('.dispatch-attachment-picker input[name="files"]').evaluate("input => input.files.length") == 2
|
||
|
||
page.goto(f"{live_server.url}{reverse('staff_tickets:sms_templates')}", wait_until="domcontentloaded")
|
||
expect(page.get_by_text("الگوی عمومی سیستم").first).to_be_visible()
|
||
|
||
page.goto(f"{live_server.url}{reverse('staff_tickets:contact_create')}", wait_until="domcontentloaded")
|
||
province = page.locator('select[name="province"]')
|
||
city = page.locator('select[name="city"]')
|
||
expect(province.locator("option")).not_to_have_count(1)
|
||
province.select_option(label="اصفهان")
|
||
expect(city.locator("option", has_text="کاشان")).to_have_count(1)
|
||
|
||
page.goto(f"{live_server.url}{reverse('staff_tickets:contact_edit', args=[customer.public_id])}", wait_until="domcontentloaded")
|
||
first_group = page.locator(f'input[name="groups"][value="{group.pk}"]')
|
||
second_group = page.locator(f'input[name="groups"][value="{secondary_group.pk}"]')
|
||
expect(first_group).to_be_checked()
|
||
expect(second_group).not_to_be_checked()
|
||
second_group.check()
|
||
first_group.uncheck()
|
||
expect(page.locator("[data-group-selection-count]")).to_contain_text("۱")
|
||
page.get_by_role("button", name="ذخیره مخاطب").click()
|
||
page.wait_for_url(f"**{reverse('staff_tickets:contacts')}")
|
||
page.goto(f"{live_server.url}{reverse('staff_tickets:contact_edit', args=[customer.public_id])}", wait_until="domcontentloaded")
|
||
expect(page.locator(f'input[name="groups"][value="{group.pk}"]')).not_to_be_checked()
|
||
expect(page.locator(f'input[name="groups"][value="{secondary_group.pk}"]')).to_be_checked()
|
||
|
||
page.goto(f"{live_server.url}{reverse('staff_tickets:create')}", wait_until="domcontentloaded")
|
||
page.locator("#staff-ticket-gallery-input").set_input_files([
|
||
{"name": "first.png", "mimeType": "image/png", "buffer": tiny_png},
|
||
{"name": "second.png", "mimeType": "image/png", "buffer": tiny_png},
|
||
])
|
||
expect(page.locator("[data-file-preview] .file-chip")).to_have_count(2)
|
||
expect(page.locator("[data-file-preview] .file-chip img")).to_have_count(2)
|
||
page.locator("[data-file-preview] .file-chip button").first.click()
|
||
expect(page.locator("[data-file-preview] .file-chip")).to_have_count(1)
|
||
assert page.locator('input[name="files"]').evaluate("input => input.files.length") == 1
|
||
|
||
|
||
def test_dispatch_report_presents_delivery_and_engagement_professionally(live_server, browser_page):
|
||
from apps.core.templatetags.jalali import jalali_datetime
|
||
|
||
unit=OrganizationalUnit.objects.create(name="فروش گزارش E2E",slug="dispatch-report-e2e")
|
||
unit.permissions.add(*Permission.objects.filter(content_type__app_label="tickets",codename__in=("view_ticket","dispatch_group_tickets")))
|
||
staff=User.objects.create_user(username="dispatch-report-agent",password=PASSWORD,account_type=User.AccountType.STAFF)
|
||
StaffProfile.objects.create(user=staff,display_name="کارشناس گزارش",primary_unit=unit)
|
||
customer=User.objects.create_user(username="dispatch-report-customer",mobile="09120009131",account_type=User.AccountType.CUSTOMER)
|
||
profile=CustomerProfile.objects.create(user=customer,first_name="مخاطب",last_name="آزمایشی")
|
||
group=ContactGroup.objects.create(unit=unit,name="گروه گزارش حرفهای")
|
||
profile.groups.add(group)
|
||
category=TicketCategory.objects.create(unit=unit,title="استعلام گزارش",slug="dispatch-report-category-e2e")
|
||
dispatch,tickets=dispatch_group_tickets(user=staff,unit=unit,contact_group=group,category=category,subject="استعلام حرفهای",body="این متن اولیه کارشناس باید بالای گزارش دیده شود.")
|
||
ticket=tickets[0]
|
||
job=NotificationJob.objects.get(ticket=ticket,event_type=SMSTemplate.Event.STAFF_TICKET_CREATED)
|
||
NotificationJob.objects.filter(pk=job.pk).update(
|
||
status=NotificationJob.Status.SENT,attempts=1,sent_at=timezone.now(),
|
||
provider_message_id="1111182358",provider_status_code="1111182358",
|
||
delivery_status=NotificationJob.DeliveryStatus.DELIVERED,delivery_status_code="5",
|
||
delivery_description="0098SMS [کد 5]: پیامک به گوشی گیرنده رسیده است.",
|
||
delivery_check_attempts=2,delivery_checked_at=timezone.now(),delivery_next_check_at=None,
|
||
)
|
||
links=[TicketMagicLink.issue_with_record(ticket,created_by=staff)[1] for _ in range(4)]
|
||
links[0].view_count=2; links[0].used_at=timezone.now(); links[0].save(update_fields=["view_count","used_at"])
|
||
|
||
with browser_page() as page:
|
||
AuthenticationPage(page,live_server.url).login_staff(staff,PASSWORD)
|
||
page.goto(f"{live_server.url}{reverse('staff_tickets:dispatch_detail',args=[dispatch.public_id])}",wait_until="domcontentloaded")
|
||
expect(page.get_by_role("heading",name="استعلام حرفهای",level=1)).to_be_visible()
|
||
expect(page.get_by_text("این متن اولیه کارشناس باید بالای گزارش دیده شود.")).to_be_visible()
|
||
expect(page.locator(".sms-state-card")).to_contain_text("تحویل به گوشی تأیید شد")
|
||
expect(page.locator(".engagement-state")).to_contain_text("2 بازدید")
|
||
expect(page.locator('[role="progressbar"]')).to_have_attribute("aria-valuenow","100")
|
||
expect(page.locator(".delivery-progress-copy")).to_contain_text("100٪")
|
||
assert float(page.locator(".sms-state-card strong").evaluate("node => parseFloat(getComputedStyle(node).fontSize)")) >= 12
|
||
assert float(page.locator(".delivery-progress-track").evaluate("node => parseFloat(getComputedStyle(node).height)")) <= 8
|
||
created_text=page.locator(".dispatch-meta-list time").inner_text()
|
||
assert created_text == jalali_datetime(dispatch.created_at)
|
||
assert "0 + 0" not in page.locator("body").inner_text()
|
||
page.get_by_text("جزئیات فنی و رهگیری").click()
|
||
expect(page.get_by_text("1111182358")).to_be_visible()
|
||
expect(page.get_by_text("موفق در اولین تلاش")).to_be_visible()
|
||
assert page.evaluate("document.documentElement.scrollWidth <= document.documentElement.clientWidth + 1")
|
||
|
||
page.set_viewport_size({"width":390,"height":844})
|
||
expect(page.locator(".dispatch-detail-table tbody tr").first).to_be_visible()
|
||
assert page.evaluate("document.documentElement.scrollWidth <= document.documentElement.clientWidth + 1")
|