156 lines
6.8 KiB
Python
156 lines
6.8 KiB
Python
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)
|
|
|
|
def open_composer(self):
|
|
launch = self.page.locator("[data-composer-toggle]")
|
|
expect(launch).to_be_visible()
|
|
launch.click()
|
|
expect(self.page.locator("[data-composer-shell]:not(.is-collapsed)")).to_be_visible()
|
|
|
|
def expect_latest_message_above_composer(self):
|
|
latest = self.page.locator("[data-message]").last
|
|
expect(latest).to_be_visible()
|
|
latest.scroll_into_view_if_needed()
|
|
message_box = latest.bounding_box()
|
|
composer_box = self.page.locator("[data-composer-shell]").bounding_box()
|
|
assert message_box and composer_box
|
|
assert message_box["y"] + message_box["height"] <= composer_box["y"] + 1
|
|
|
|
@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.page.locator('[data-wizard-step="1"]')).to_be_visible()
|
|
expect(self.page.locator('[data-ticket-subject]')).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.evaluate(
|
|
"(node, value) => { node.value = value; node.dispatchEvent(new Event('change', {bubbles: true})); }",
|
|
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.evaluate(
|
|
"node => { node.checked = true; node.dispatchEvent(new Event('change', {bubbles: true})); }"
|
|
)
|
|
expect(radio).to_be_checked()
|
|
return radio.get_attribute("value")
|
|
|
|
def select_general_queue(self):
|
|
radio = self.queue.locator('input[name="selected_expert"]')
|
|
radio.evaluate(
|
|
"node => { node.checked = true; node.dispatchEvent(new Event('change', {bubbles: true})); }"
|
|
)
|
|
expect(radio).to_be_checked()
|
|
|
|
def select_category(self, category_id):
|
|
self.category.evaluate(
|
|
"(node, value) => { node.value = value; node.dispatchEvent(new Event('change', {bubbles: true})); }",
|
|
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.locator("button").evaluate("node => node.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('[data-wizard-step="1"] [data-wizard-next]').click()
|
|
expect(self.page.locator('[data-wizard-step="2"]')).to_be_visible()
|
|
self.page.locator('[data-wizard-step="2"] [data-wizard-next]').click()
|
|
expect(self.page.locator('[data-wizard-step="3"]')).to_be_visible()
|
|
self.page.locator('[data-wizard-step="3"] [data-wizard-next]').click()
|
|
expect(self.page.locator('[data-wizard-step="4"]')).to_be_visible()
|
|
self.page.locator('textarea[name="body"]').fill(body)
|
|
self.page.locator('[data-wizard-step="4"] [data-wizard-next]').click()
|
|
expect(self.page.locator('[data-wizard-step="5"]')).to_be_visible()
|
|
expect(self.page.locator('[data-review-subject]')).to_have_text(subject)
|
|
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)
|