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("Таблица пуста или не найдена")