This commit is contained in:
javad committed 2026-09-10 11:34:19 +03:30
commit fa9d154db9
260 files changed
+17643

No files matched your search

+1
View File
@@ -0,0 +1 @@
"""Reusable page objects for browser-level tests."""
+20
View File
@@ -0,0 +1,20 @@
from django.urls import reverse
from playwright.sync_api import expect
from tests.e2e.pages.base import BasePage
class AuthenticationPage(BasePage):
def login_customer(self, customer, password):
self.goto(reverse("accounts:password_login"))
self.page.locator('input[name="mobile"]').fill(customer.local_mobile)
self.page.locator('input[name="password"]').fill(password)
self.page.get_by_role("button", name="ورود به حساب").click()
expect(self.page).to_have_url(self.url(reverse("tickets:list")))
def login_staff(self, staff, password):
self.goto(reverse("accounts:staff_login"))
self.page.locator('input[name="username"]').fill(staff.username)
self.page.locator('input[name="password"]').fill(password)
self.page.get_by_role("button", name="ورود به میز کار").click()
expect(self.page).to_have_url(self.url(reverse("staff_tickets:list")))
+13
View File
@@ -0,0 +1,13 @@
from urllib.parse import urljoin
class BasePage:
def __init__(self, page, base_url):
self.page = page
self.base_url = base_url.rstrip("/") + "/"
def url(self, path):
return urljoin(self.base_url, path.lstrip("/"))
def goto(self, path):
return self.page.goto(self.url(path), wait_until="domcontentloaded")
+31
View File
@@ -0,0 +1,31 @@
import re
from playwright.sync_api import expect
from tests.e2e.pages.base import BasePage
class CustomerNewTicketPage(BasePage):
def __init__(self, page, live_server_url: str):
super().__init__(page, live_server_url)
self.unit=page.locator("[data-ticket-unit]")
self.category=page.locator("[data-ticket-category]")
def open(self):
self.goto("/tickets/new/")
expect(self.unit).to_be_visible()
def select_unit(self, unit_id: int, expected_category: str):
with self.page.expect_response(lambda response: "/tickets/new/options/" in response.url and response.ok):
self.unit.select_option(str(unit_id))
expect(self.category.locator("option", has_text=expected_category)).to_have_count(1)
def search_and_choose(self, query: str, category_name: str):
self.page.locator("[data-ticket-search]").fill(query)
result=self.page.get_by_role(
"button", name=re.compile(rf"^{re.escape(category_name)}")
)
expect(result).to_be_visible()
result.click()
option = self.category.get_by_role("option", name=category_name, exact=True)
expect(option).to_have_count(1)
expect(self.category).to_have_value(option.get_attribute("value"))
+120
View File
@@ -0,0 +1,120 @@
import re
from playwright.sync_api import expect
from tests.e2e.pages.base import BasePage
class TicketConversationPage(BasePage):
guidance_text = "لطفاً همه توضیحات و سؤال‌های خود را در یک پیام کامل بنویسید و سپس ارسال کنید."
def send_reply(self, body):
self.page.locator("[data-composer-toggle]").click()
reply = self.page.locator('form[data-chat-coach] textarea[name="body"]')
expect(reply).to_be_visible()
reply.fill(body)
with self.page.expect_navigation(wait_until="domcontentloaded"):
self.page.locator('form[data-chat-coach] [data-primary-submit]').click()
assert "reply_result=reply" in self.page.url
expect(self.page.locator("[data-conversation]")).to_contain_text(body)
@property
def chat_guidance(self):
return self.page.locator(".chat-coach-floating")
def expect_guidance_visible(self):
expect(self.chat_guidance).to_be_visible()
expect(self.chat_guidance).to_contain_text(self.guidance_text)
def expect_guidance_hidden(self):
expect(self.chat_guidance).to_be_hidden()
def dismiss_guidance(self):
self.chat_guidance.get_by_role("button", name="بستن راهنما").click()
self.expect_guidance_hidden()
class PublicTicketCreatePage(BasePage):
def open(self):
self.goto("/tickets/new/")
expect(self.unit).to_be_visible()
@property
def unit(self):
return self.page.locator("[data-ticket-unit]")
@property
def expert_picker(self):
return self.page.locator("[data-expert-picker]")
@property
def category(self):
return self.page.locator("[data-ticket-category]")
@property
def queue(self):
return self.page.locator("[data-general-queue]")
@property
def file_preview(self):
return self.page.locator("[data-file-preview]")
def select_unit(self, unit_id):
unit_id = str(unit_id)
with self.page.expect_response(
lambda response: "/tickets/new/options/" in response.url
and f"unit={unit_id}" in response.url
and response.status == 200
) as response_info:
self.unit.select_option(unit_id)
payload = response_info.value.json()
expect(self.page.locator("[data-general-queue]")).to_have_attribute("data-unit-id", unit_id)
expected_ids = {str(item["id"]) for item in payload["experts"]}
actual_ids = set(self.expert_picker.locator('input[name="selected_expert"]:not([value=""])').evaluate_all("nodes => nodes.map(node => node.value)"))
assert actual_ids == expected_ids
return payload
def select_search_result(self, query, result_text):
self.page.locator("[data-ticket-search]").fill(query)
result = self.page.locator("[data-ticket-search-results] button").filter(has_text=result_text).first
expect(result).to_be_visible()
result.click()
def select_expert(self, display_name):
card = self.expert_picker.locator("label.expert-card").filter(has_text=display_name)
expect(card).to_have_count(1)
radio = card.locator('input[name="selected_expert"]')
radio.check()
expect(radio).to_be_checked()
return radio.get_attribute("value")
def select_general_queue(self):
radio = self.queue.locator('input[name="selected_expert"]')
radio.check()
expect(radio).to_be_checked()
def select_category(self, category_id):
self.category.select_option(str(category_id))
expect(self.category).to_have_value(str(category_id))
def upload_gallery(self, files, *, total_count=None):
self.page.locator("[data-gallery-input]").set_input_files(files)
expect(self.file_preview.locator(".file-chip")).to_have_count(total_count or len(files))
def upload_documents(self, files, *, total_count=None):
self.page.locator("[data-document-input]").set_input_files(files)
expect(self.file_preview.locator(".file-chip")).to_have_count(total_count or len(files))
def remove_attachment(self, filename):
chip = self.file_preview.locator(".file-chip").filter(has_text=filename)
expect(chip).to_have_count(1)
chip.get_by_role("button", name="حذف فایل").click()
expect(self.file_preview.locator(".file-chip").filter(has_text=filename)).to_have_count(0)
def submit(self, *, subject, body):
self.page.locator('input[name="subject"]').fill(subject)
self.page.locator('textarea[name="body"]').fill(body)
with self.page.expect_navigation(wait_until="domcontentloaded"):
self.page.get_by_role("button", name="ثبت و ارسال درخواست").click()
expect(self.page).to_have_url(re.compile(r"/tickets/(?!new/)[^/]+/$"))
expect(self.page.locator("h1")).to_have_text(subject)