296 lines
13 KiB
Python
296 lines
13 KiB
Python
from playwright.sync_api import Page, TimeoutError, Response, APIRequestContext, expect
|
||
from data.environment import host
|
||
from Locators.base_page import BasePageLocators
|
||
import time
|
||
import json
|
||
|
||
class BasePage:
|
||
def __init__(self, page: Page):
|
||
self.page = page
|
||
self.token = ""
|
||
|
||
def open(self, uri) -> Response | None:
|
||
return self.page.goto(f"{host.get_base_url()}{uri}", wait_until='domcontentloaded')
|
||
|
||
def get_api_request_context(self) -> APIRequestContext:
|
||
return self.page.context.request
|
||
|
||
#return page url
|
||
def current_url(self) -> str:
|
||
return self.page.url
|
||
|
||
# click on element
|
||
def click(self, locator: str) -> None:
|
||
self.page.click(locator)
|
||
|
||
# input in field
|
||
def input(self, locator: str, data: str) -> None:
|
||
self.page.locator(locator).fill(data)
|
||
|
||
# clear input field
|
||
def clear_input(self, locator: str) -> None:
|
||
self.page.locator(locator).press('Control+A')
|
||
self.page.locator(locator).press('Backspace')
|
||
|
||
def page_reload(self) ->None:
|
||
self.page.reload()
|
||
|
||
# wait for element
|
||
def wait_for_element(self, locator, timeout=12000) -> None:
|
||
self.page.wait_for_selector(locator, timeout=timeout)
|
||
|
||
# wait for all elements
|
||
def wait_for_all_elements(self, locator, timeout=5000):
|
||
elements = self.page.query_selector_all(locator)
|
||
|
||
for element in elements:
|
||
self.page.wait_for_selector(locator, timeout=timeout)
|
||
|
||
return elements
|
||
|
||
# if element presents then ok
|
||
def is_element_present(self, locator: str, timeout: int = 5000) -> bool:
|
||
try:
|
||
self.page.wait_for_selector(locator, timeout=timeout)
|
||
except TimeoutError:
|
||
return False
|
||
return True
|
||
|
||
# if element is not present then ok
|
||
def is_element_NOT_presence(self, locator: str, timeout: int = 5000) -> bool:
|
||
try:
|
||
self.page.wait_for_selector(locator, timeout=timeout)
|
||
except TimeoutError:
|
||
return True
|
||
return False
|
||
|
||
# get text. If we have once locator then index is 0
|
||
def get_text(self, locator: str, index: int) -> str:
|
||
return self.page.locator(locator).nth(index).text_content()
|
||
|
||
# Traverse the table
|
||
def read_table_data(self, locator) -> []:
|
||
table_data = []
|
||
|
||
table = self.page.locator(locator)
|
||
|
||
# read table header
|
||
header_cells = table.locator(BasePageLocators.TABLE_HEADERS_CELLS)
|
||
header_data = []
|
||
for h in range(header_cells.count()):
|
||
header_cell_text = header_cells.nth(h).inner_text()
|
||
header_data.append(header_cell_text)
|
||
table_data.append(header_data)
|
||
|
||
# read table cells by rows
|
||
rows = table.locator(BasePageLocators.TABLE_BODY)
|
||
for i in range(rows.count()):
|
||
row = rows.nth(i)
|
||
cells = row.locator("td")
|
||
row_data = []
|
||
for j in range(cells.count()):
|
||
cell_text = cells.nth(j).inner_text()
|
||
row_data.append(cell_text)
|
||
table_data.append(row_data)
|
||
|
||
return table_data
|
||
|
||
# send get request to api
|
||
def send_get_api_request(self, uri):
|
||
api_request_context = self.get_api_request_context()
|
||
token = host.get_access_token()
|
||
headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
|
||
response = api_request_context.get(f"{host.get_base_url()}{uri}", headers = headers)
|
||
return response
|
||
|
||
# send get request to api
|
||
def send_post_api_request(self, uri, payload):
|
||
api_request_context = self.get_api_request_context()
|
||
token = host.get_access_token()
|
||
headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
|
||
response = api_request_context.post(f"{host.get_base_url()}{uri}", headers = headers, data=payload)
|
||
return response
|
||
|
||
# get response body
|
||
def get_response_body(self, response):
|
||
try:
|
||
response_body = response.json()
|
||
except json.JSONDecodeError:
|
||
print("Failed to decode JSON response")
|
||
return None
|
||
return response_body
|
||
|
||
def should_be_horizontal_scroll(self) -> None:
|
||
"""
|
||
Проверяет горизонтальный скролл с плавной прокруткой.
|
||
"""
|
||
modal_selector = "div.v-dialog.v-dialog--active"
|
||
modal = self.page.locator(modal_selector).first
|
||
|
||
try:
|
||
# Ожидаем появления окна
|
||
modal.wait_for(state="visible", timeout=5000)
|
||
|
||
# Получаем размеры через page.evaluate
|
||
scroll_data = self.page.evaluate("""() => {
|
||
const modal = document.querySelector('div.v-dialog.v-dialog--active');
|
||
return {
|
||
scrollWidth: modal.scrollWidth,
|
||
clientWidth: modal.clientWidth,
|
||
scrollLeft: modal.scrollLeft
|
||
};
|
||
}""")
|
||
|
||
print(f"Окно: Общая ширина: {scroll_data['scrollWidth']}px, "
|
||
f"Видимая область: {scroll_data['clientWidth']}px, "
|
||
f"Начальная позиция: {scroll_data['scrollLeft']}px")
|
||
|
||
if scroll_data['scrollWidth'] > scroll_data['clientWidth']:
|
||
print("Тестируем скролл...")
|
||
|
||
# Плавный скролл вправо
|
||
self.page.evaluate("""() => {
|
||
const modal = document.querySelector('div.v-dialog.v-dialog--active');
|
||
return new Promise(resolve => {
|
||
const target = modal.scrollWidth - modal.clientWidth;
|
||
const duration = 1000;
|
||
const start = modal.scrollLeft;
|
||
const startTime = performance.now();
|
||
|
||
function scrollStep(timestamp) {
|
||
const progress = (timestamp - startTime) / duration;
|
||
modal.scrollLeft = start + (target - start) * Math.min(progress, 1);
|
||
if (progress < 1) {
|
||
window.requestAnimationFrame(scrollStep);
|
||
} else {
|
||
resolve();
|
||
}
|
||
}
|
||
window.requestAnimationFrame(scrollStep);
|
||
});
|
||
}""")
|
||
|
||
# Проверяем конечную позицию
|
||
final_scroll = self.page.evaluate("""() => {
|
||
return document.querySelector('div.v-dialog.v-dialog--active').scrollLeft;
|
||
}""")
|
||
print("Успешно проскроллили вправо")
|
||
|
||
# Плавный скролл влево
|
||
self.page.evaluate("""() => {
|
||
const modal = document.querySelector('div.v-dialog.v-dialog--active');
|
||
return new Promise(resolve => {
|
||
const duration = 1000;
|
||
const start = modal.scrollLeft;
|
||
const startTime = performance.now();
|
||
|
||
function scrollStep(timestamp) {
|
||
const progress = (timestamp - startTime) / duration;
|
||
modal.scrollLeft = start - start * Math.min(progress, 1);
|
||
if (progress < 1) {
|
||
window.requestAnimationFrame(scrollStep);
|
||
} else {
|
||
resolve();
|
||
}
|
||
}
|
||
window.requestAnimationFrame(scrollStep);
|
||
});
|
||
}""")
|
||
|
||
# Проверяем возврат в начало
|
||
final_scroll = self.page.evaluate("""() => {
|
||
return document.querySelector('div.v-dialog.v-dialog--active').scrollLeft;
|
||
}""")
|
||
assert final_scroll == 0, "Не удалось вернуться в начальную позицию скролла"
|
||
print("Успешно проскроллили влево")
|
||
else:
|
||
print("Окно не требует горизонтального скролла.")
|
||
|
||
except Exception as e:
|
||
print(f"Ошибка при проверке скролла окна: {str(e)}")
|
||
raise
|
||
|
||
def should_be_vertical_scroll(self, wrapper_selector: str = "div.scrolltable__wrapper") -> None:
|
||
"""
|
||
Проверяет вертикальный скролл в указанном контейнере.
|
||
Args:
|
||
wrapper_selector: CSS-селектор скроллируемого контейнера.
|
||
По умолчанию: "div.scrolltable__wrapper".
|
||
"""
|
||
# Ожидаем загрузки таблицы
|
||
table_wrapper = self.page.locator(wrapper_selector).first
|
||
table_wrapper.wait_for(state="visible", timeout=10000)
|
||
|
||
try:
|
||
# Пытаемся найти скроллируемый элемент с небольшим таймаутом
|
||
scrollable_element = table_wrapper.locator("tbody.scrolltable__scroll").first
|
||
scrollable_element.wait_for(state="attached", timeout=3000)
|
||
|
||
# Проверяем параметры скролла
|
||
scroll_height = scrollable_element.evaluate("el => el.scrollHeight")
|
||
client_height = scrollable_element.evaluate("el => el.clientHeight")
|
||
scroll_data = scrollable_element.evaluate("el => el.scrollLeft")
|
||
|
||
|
||
print(f"Окно: Общая ширина: {scroll_height}px, "
|
||
f"Видимая область: {client_height}px, "
|
||
f"Начальная позиция: {scroll_data}px")
|
||
|
||
if scroll_height > client_height:
|
||
print("Тестируем скролл...")
|
||
|
||
# 1. Плавный скролл вниз
|
||
scrollable_element.evaluate("""el => {
|
||
const target = el.scrollHeight;
|
||
const duration = 1000;
|
||
const start = el.scrollTop;
|
||
const startTime = performance.now();
|
||
|
||
function scrollStep(timestamp) {
|
||
const progress = (timestamp - startTime) / duration;
|
||
el.scrollTop = start + (target - start) * Math.min(progress, 1);
|
||
if (progress < 1) {
|
||
window.requestAnimationFrame(scrollStep);
|
||
}
|
||
}
|
||
window.requestAnimationFrame(scrollStep);
|
||
}""")
|
||
time.sleep(1.5) # Ожидаем завершения скролла
|
||
|
||
# Проверяем последнюю строку
|
||
last_row = table_wrapper.locator("tbody tr").last
|
||
expect(last_row).to_be_visible()
|
||
print("Успешно проскроллили вниз")
|
||
|
||
# 2. Плавный скролл вверх
|
||
scrollable_element.evaluate("""el => {
|
||
const duration = 1000;
|
||
const start = el.scrollTop;
|
||
const startTime = performance.now();
|
||
|
||
function scrollStep(timestamp) {
|
||
const progress = (timestamp - startTime) / duration;
|
||
el.scrollTop = start - start * Math.min(progress, 1);
|
||
if (progress < 1) {
|
||
window.requestAnimationFrame(scrollStep);
|
||
}
|
||
}
|
||
window.requestAnimationFrame(scrollStep);
|
||
}""")
|
||
time.sleep(1.5) # Ожидаем завершения скролла
|
||
|
||
# Проверяем первую строку
|
||
first_row = table_wrapper.locator("tbody tr").first
|
||
expect(first_row).to_be_visible()
|
||
print("Успешно проскроллили вверх")
|
||
else:
|
||
print("Весь контент виден без скролла")
|
||
|
||
except Exception as e:
|
||
print(f"Элемент для скролла не найден или не скроллится: {e}")
|
||
# Проверяем, есть ли хотя бы строки в таблице
|
||
rows = table_wrapper.locator("tbody tr")
|
||
if rows.count() > 0:
|
||
print(f"Найдено {rows.count()} строк в таблице без скролла")
|
||
else:
|
||
print("Таблица пуста или не найдена") |