159 lines
8.3 KiB
Python
159 lines
8.3 KiB
Python
import pytest
|
|
from django.urls import reverse
|
|
from playwright.sync_api import expect
|
|
|
|
from apps.accounts.models import TicketMagicLink, User
|
|
from apps.core.models import SiteBranding
|
|
from apps.customers.models import CustomerProfile
|
|
from apps.messaging.models import Message
|
|
from apps.staff.models import StaffProfile
|
|
from apps.tickets.models import TicketCategory
|
|
from apps.tickets.services import create_ticket
|
|
from tests.e2e.pages.auth import AuthenticationPage
|
|
from tests.e2e.pages.tickets import TicketConversationPage
|
|
|
|
|
|
pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)]
|
|
PASSWORD = "E2e-safe-password-2026"
|
|
|
|
|
|
def assert_workspace_fits_visual_viewport(page):
|
|
metrics = page.evaluate(
|
|
"""() => {
|
|
const box = document.querySelector('.ticket-workspace').getBoundingClientRect();
|
|
const viewportHeight = window.visualViewport?.height || window.innerHeight;
|
|
return {
|
|
top: box.top,
|
|
bottom: box.bottom,
|
|
viewportHeight,
|
|
pageWidth: document.documentElement.scrollWidth,
|
|
viewportWidth: document.documentElement.clientWidth,
|
|
};
|
|
}"""
|
|
)
|
|
assert metrics["top"] >= -1
|
|
assert metrics["bottom"] <= metrics["viewportHeight"] + 1
|
|
assert metrics["pageWidth"] <= metrics["viewportWidth"] + 1
|
|
|
|
|
|
def assert_reply_input_is_visible(page):
|
|
metrics = page.evaluate(
|
|
"""() => {
|
|
const input = document.querySelector('form[data-ticket-composer] textarea');
|
|
const composer = document.querySelector('[data-composer-shell]');
|
|
const inputBox = input.getBoundingClientRect();
|
|
const composerBox = composer.getBoundingClientRect();
|
|
const viewportTop = window.visualViewport?.offsetTop || 0;
|
|
const viewportBottom = viewportTop + (window.visualViewport?.height || window.innerHeight);
|
|
const visibleTop = Math.max(inputBox.top, composerBox.top, viewportTop);
|
|
const visibleBottom = Math.min(inputBox.bottom, composerBox.bottom, viewportBottom);
|
|
return {visibleHeight: Math.max(0, visibleBottom - visibleTop), inputHeight: inputBox.height};
|
|
}"""
|
|
)
|
|
assert metrics["visibleHeight"] >= min(64, metrics["inputHeight"] * 0.6)
|
|
|
|
|
|
def assert_compact_composer_controls(page):
|
|
metrics = page.evaluate(
|
|
"""() => {
|
|
const input = document.querySelector('form[data-ticket-composer] textarea').getBoundingClientRect();
|
|
const jump = document.querySelector('[data-scroll-to-submit]').getBoundingClientRect();
|
|
const pickers = [...document.querySelectorAll('.media-picker-button')].map(node => node.getBoundingClientRect());
|
|
return {
|
|
inputBottom: input.bottom,
|
|
jumpBottom: jump.bottom,
|
|
pickerTopDifference: Math.abs(pickers[0].top - pickers[1].top),
|
|
pickersOverlap: !(pickers[0].right <= pickers[1].left || pickers[1].right <= pickers[0].left),
|
|
};
|
|
}"""
|
|
)
|
|
assert abs(metrics["inputBottom"] - metrics["jumpBottom"]) <= 8
|
|
assert metrics["pickerTopDifference"] <= 2
|
|
assert not metrics["pickersOverlap"]
|
|
|
|
|
|
def assert_textarea_grows_with_content(page):
|
|
reply = page.locator('form[data-ticket-composer] textarea')
|
|
reply.fill("پاسخ کوتاه")
|
|
compact_height = reply.bounding_box()["height"]
|
|
reply.fill("خط اول\nخط دوم\nخط سوم\nخط چهارم\nخط پنجم\nخط ششم\nخط هفتم")
|
|
grown_height = reply.bounding_box()["height"]
|
|
assert compact_height <= 52
|
|
assert compact_height < grown_height <= 145
|
|
|
|
|
|
def test_magic_link_mobile_viewport_keeps_composer_messages_and_credit_visible(live_server, browser_page):
|
|
branding = SiteBranding.load()
|
|
branding.developer_credit_text = "امضای مقدس توسعهدهنده E2E"
|
|
branding.save()
|
|
category = TicketCategory.objects.create(title="موبایل لینک امن", slug="magic-mobile-viewport")
|
|
customer = User.objects.create_user(username="magic-mobile", mobile="09120009101", account_type=User.AccountType.CUSTOMER)
|
|
CustomerProfile.objects.create(user=customer, first_name="مشتری", last_name="لینک امن")
|
|
staff = User.objects.create_user(username="magic-mobile-staff", mobile="09120009102", account_type=User.AccountType.STAFF)
|
|
StaffProfile.objects.create(user=staff, display_name="کارشناس لینک امن")
|
|
ticket = create_ticket(customer=customer, category=category, subject="بررسی موبایل لینک امن", body="پیام آغازین")
|
|
for index in range(12):
|
|
Message.objects.create(ticket=ticket, author=staff, body=f"پاسخ کارشناس شماره {index + 1}")
|
|
token = TicketMagicLink.issue(ticket)
|
|
|
|
with browser_page() as page:
|
|
page.set_viewport_size({"width": 390, "height": 844})
|
|
page.goto(f"{live_server.url}{reverse('accounts:magic', args=[token])}", wait_until="domcontentloaded")
|
|
conversation = TicketConversationPage(page, live_server.url)
|
|
credit = page.locator(".ticket-detail-credit")
|
|
expect(credit).to_be_visible()
|
|
expect(credit).to_contain_text("امضای مقدس توسعهدهنده E2E")
|
|
assert_workspace_fits_visual_viewport(page)
|
|
conversation.expect_latest_message_above_composer()
|
|
|
|
conversation.open_composer()
|
|
assert_compact_composer_controls(page)
|
|
page.set_viewport_size({"width": 390, "height": 430})
|
|
expect(page.locator('form[data-ticket-composer] textarea')).to_be_focused()
|
|
expect(page.locator("[data-composer-shell]")).to_have_attribute("data-keyboard-open", "true")
|
|
assert_textarea_grows_with_content(page)
|
|
assert_reply_input_is_visible(page)
|
|
expect(page.locator("[data-composer-close]")).to_be_hidden()
|
|
expect(credit).to_be_visible()
|
|
assert_workspace_fits_visual_viewport(page)
|
|
|
|
page.set_viewport_size({"width": 390, "height": 844})
|
|
expect(page.locator("[data-composer-shell].is-collapsed")).to_be_visible()
|
|
expect(credit).to_be_visible()
|
|
assert_workspace_fits_visual_viewport(page)
|
|
conversation.expect_latest_message_above_composer()
|
|
|
|
|
|
def test_staff_ticket_mobile_viewport_survives_open_close_and_rotation(live_server, browser_page):
|
|
category = TicketCategory.objects.create(title="موبایل کارشناس", slug="staff-mobile-viewport")
|
|
customer = User.objects.create_user(username="staff-mobile-customer", mobile="09120009103", account_type=User.AccountType.CUSTOMER)
|
|
CustomerProfile.objects.create(user=customer, first_name="مشتری", last_name="پنل کارشناس")
|
|
staff = User.objects.create_user(username="staff-mobile-user", mobile="09120009104", password=PASSWORD, account_type=User.AccountType.STAFF)
|
|
StaffProfile.objects.create(user=staff, display_name="کارشناس موبایل")
|
|
ticket = create_ticket(customer=customer, category=category, subject="بررسی موبایل پنل کارشناس", selected_expert=staff, body="شرح درخواست مشتری")
|
|
for index in range(12):
|
|
Message.objects.create(ticket=ticket, author=customer, body=f"پیام مشتری شماره {index + 1}")
|
|
|
|
with browser_page() as page:
|
|
page.set_viewport_size({"width": 390, "height": 844})
|
|
AuthenticationPage(page, live_server.url).login_staff(staff, PASSWORD)
|
|
conversation = TicketConversationPage(page, live_server.url)
|
|
conversation.goto(reverse("staff_tickets:detail", args=[ticket.public_id]))
|
|
expect(page.get_by_role("button", name="ثبت اقدام جدید در تیکت")).to_be_visible()
|
|
assert_workspace_fits_visual_viewport(page)
|
|
conversation.expect_latest_message_above_composer()
|
|
|
|
conversation.open_composer()
|
|
page.set_viewport_size({"width": 844, "height": 390})
|
|
assert_workspace_fits_visual_viewport(page)
|
|
expect(page.locator("[data-composer-shell]")).to_have_attribute("data-keyboard-open", "true")
|
|
assert_compact_composer_controls(page)
|
|
assert_textarea_grows_with_content(page)
|
|
assert_reply_input_is_visible(page)
|
|
expect(page.locator("[data-composer-close]")).to_be_visible()
|
|
|
|
page.locator("[data-composer-close]").click()
|
|
expect(page.locator("[data-composer-shell].is-collapsed")).to_be_visible()
|
|
assert_workspace_fits_visual_viewport(page)
|
|
conversation.expect_latest_message_above_composer()
|