Skip to content

BasePage

Базовый класс страницы для работы с Playwright.

Содержит общие методы для взаимодействия со страницей и API.

BasePage

Базовый класс для работы со страницами через Playwright.

Атрибуты

page (Page): Экземпляр страницы Playwright.

Source code in pages\base_page.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
class BasePage:
    """Базовый класс для работы со страницами через Playwright.

    Атрибуты:
        page (Page): Экземпляр страницы Playwright.
    """

    def __init__(self, page: Page):
        """Инициализирует базовую страницу.

        Args:
            page (Page): Экземпляр страницы Playwright.
        """
        self.page = page

    # Действия:
    def current_url(self) -> str:
        """Возвращает текущий URL страницы.

        Returns:
            str: Текущий URL страницы.
        """
        return self.page.url

    def open(self, uri) -> Response | None:
        """Открывает указанный URI в браузере.

        Args:
            uri (str): URI для открытия (без базового URL).

        Returns:
            Response | None: Ответ сервера или None в случае ошибки.
        """
        return self.page.goto(f"{host.get_base_url()}{uri}", wait_until='domcontentloaded')

    def page_reload(self) -> None:
        """Перезагружает текущую страницу."""
        self.page.reload()

    def wait_for_timeout(self, timeout: int) -> None:
        """Ожидает указанное количество миллисекунд.

        Args:
            timeout (int): Время ожидания в миллисекундах.
        """
        self.page.wait_for_timeout(timeout)

    def get_api_request_context(self) -> APIRequestContext:
        """Возвращает контекст API-запросов.

        Returns:
            APIRequestContext: Контекст для выполнения API-запросов.
        """
        return self.page.context.request

    def send_get_api_request(self, uri: str) -> Response:
        """Отправляет GET-запрос к API.

        Args:
            uri (str): URI API-эндпоинта (без базового URL).

        Returns:
            Response: Ответ сервера.
        """
        api_request_context = self.get_api_request_context()
        token = host.get_access_token()

        # Проверяем что токен получен
        if not token:
            logger.error("Failed to get access token: token is None or empty")
            # Возвращаем заглушечный response или бросаем исключение
            # В Playwright можно создать mock response если нужно
            return None

        headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
        full_url = f"{host.get_request_url()}{uri}"

        logger.debug("Sending GET request to: %s", full_url)
        response = api_request_context.get(full_url, headers=headers)

        # Логируем статус ответа
        logger.debug("GET response status: %s", response.status)

        return response

    def send_post_api_request(self, uri: str, payload: Dict) -> Response:
        """Отправляет POST-запрос к API."""
        api_request_context = self.get_api_request_context()
        token = host.get_access_token()

        if not token:
            logger.error("Failed to get access token: token is None or empty")
            return None

        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "Authorization": f"Bearer {token}"
        }
        full_url = f"{host.get_request_url()}{uri}"

        logger.debug("Sending POST request to: %s", full_url)

        # Сериализуем payload в JSON
        json_data = json.dumps(payload)

        # Проверяем что сериализация прошла успешно
        if json_data is None:
            logger.error("Failed to serialize payload to JSON: result is None")
            return None

        if not isinstance(json_data, str):
            logger.error("Failed to serialize payload to JSON: expected string got %s", type(json_data))
            return None

        response = api_request_context.post(
            full_url,
            headers=headers,
            data=json_data  # Передаем сериализованный JSON как data
        )

        logger.debug("POST response status: %s", response.status)

        return response

    def get_response_body(self, response: Response) -> dict | list | None:
        """Извлекает тело ответа в format JSON.

        Args:
            response (Response): Ответ сервера.

        Returns:
            dict | list | None: Распарсенное тело ответа или None в случае ошибки.
        """
        start_time = time.time()

        # Проверяем что response не None
        if response is None:
            logger.error("Response object is None")
            processing_time = time.time() - start_time
            logger.debug("Response processing time1: %.3f seconds", processing_time)
            return None

        # Проверяем статус ответа
        if response.status >= 400:
            logger.error("API request failed with status %s", response.status)
            processing_time = time.time() - start_time
            logger.debug("Response processing time2: %.3f seconds", processing_time)
            return None

        # Пытаемся получить JSON
        json_result = response.json()

        # Проверяем что результат не None
        if json_result is None:
            logger.error("JSON parsing returned None")
            processing_time = time.time() - start_time
            logger.debug("Response processing time3: %.3f seconds", processing_time)
            return None

        # Принимаем как словари, так и списки
        if not isinstance(json_result, (dict, list)):
            logger.error("Expected dict or list but got %s", type(json_result))
            processing_time = time.time() - start_time
            logger.debug("Response processing time4: %.3f seconds", processing_time)
            return None

        processing_time = time.time() - start_time
        logger.debug("Response processing time5: %.3f seconds", processing_time)

        return json_result

        # Проверки:
    def check_URL(self, uri: str, msg: str) -> None:
        """Проверяет, что текущий URL соответствует ожидаемому.

        Args:
            uri (str): Ожидаемый URI (без базового URL).
            msg (str): Сообщение об ошибке при несоответствии.

        Raises:
            AssertionError: Если URL не соответствует ожидаемому.
        """
        expect(self.page).to_have_url(  # pylint: disable=expression-not-assigned
            f"{host.get_base_url()}{uri}",
            timeout=60000
        ), msg

    def check_equals(self, actual: Any, expected: Any, msg: str) -> None:
        """Проверяет равенство фактического и ожидаемого значений.

        Args:
            actual (Any): Фактическое значение.
            expected (Any): Ожидаемое значение.
            msg (str): Сообщение об ошибке при несоответствии.

        Raises:
            AssertionError: Если значения не равны.
        """
        assert actual == expected, msg

    def check_lists_equals(self, actual: list, expected: list, msg: str) -> None:
        """Рекурсивно проверяет равенство двух списков.

        Args:
            actual (list): Фактический список.
            expected (list): Ожидаемый список.
            msg (str): Сообщение об ошибке при несоответствии.

        Raises:
            AssertionError: Если списки не равны.
        """
        def compare_lists(list1: list, list2: list) -> bool:
            """Вспомогательная функция для рекурсивного сравнения списков.

            Args:
                list1 (list): Первый список для сравнения.
                list2 (list): Второй список для сравнения.

            Returns:
                bool: True если списки идентичны, иначе False.
            """
            if len(list1) != len(list2):
                return False
            for item1, item2 in zip(list1, list2):
                if isinstance(item1, list) and isinstance(item2, list):
                    if not compare_lists(item1, item2):
                        return False
                elif item1 != item2:
                    return False
            return True

        assert compare_lists(actual, expected), msg

__init__(page)

Инициализирует базовую страницу.

Parameters:

Name Type Description Default
page Page

Экземпляр страницы Playwright.

required
Source code in pages\base_page.py
22
23
24
25
26
27
28
def __init__(self, page: Page):
    """Инициализирует базовую страницу.

    Args:
        page (Page): Экземпляр страницы Playwright.
    """
    self.page = page

check_URL(uri, msg)

Проверяет, что текущий URL соответствует ожидаемому.

Parameters:

Name Type Description Default
uri str

Ожидаемый URI (без базового URL).

required
msg str

Сообщение об ошибке при несоответствии.

required

Raises:

Type Description
AssertionError

Если URL не соответствует ожидаемому.

Source code in pages\base_page.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def check_URL(self, uri: str, msg: str) -> None:
    """Проверяет, что текущий URL соответствует ожидаемому.

    Args:
        uri (str): Ожидаемый URI (без базового URL).
        msg (str): Сообщение об ошибке при несоответствии.

    Raises:
        AssertionError: Если URL не соответствует ожидаемому.
    """
    expect(self.page).to_have_url(  # pylint: disable=expression-not-assigned
        f"{host.get_base_url()}{uri}",
        timeout=60000
    ), msg

check_equals(actual, expected, msg)

Проверяет равенство фактического и ожидаемого значений.

Parameters:

Name Type Description Default
actual Any

Фактическое значение.

required
expected Any

Ожидаемое значение.

required
msg str

Сообщение об ошибке при несоответствии.

required

Raises:

Type Description
AssertionError

Если значения не равны.

Source code in pages\base_page.py
203
204
205
206
207
208
209
210
211
212
213
214
def check_equals(self, actual: Any, expected: Any, msg: str) -> None:
    """Проверяет равенство фактического и ожидаемого значений.

    Args:
        actual (Any): Фактическое значение.
        expected (Any): Ожидаемое значение.
        msg (str): Сообщение об ошибке при несоответствии.

    Raises:
        AssertionError: Если значения не равны.
    """
    assert actual == expected, msg

check_lists_equals(actual, expected, msg)

Рекурсивно проверяет равенство двух списков.

Parameters:

Name Type Description Default
actual list

Фактический список.

required
expected list

Ожидаемый список.

required
msg str

Сообщение об ошибке при несоответствии.

required

Raises:

Type Description
AssertionError

Если списки не равны.

Source code in pages\base_page.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def check_lists_equals(self, actual: list, expected: list, msg: str) -> None:
    """Рекурсивно проверяет равенство двух списков.

    Args:
        actual (list): Фактический список.
        expected (list): Ожидаемый список.
        msg (str): Сообщение об ошибке при несоответствии.

    Raises:
        AssertionError: Если списки не равны.
    """
    def compare_lists(list1: list, list2: list) -> bool:
        """Вспомогательная функция для рекурсивного сравнения списков.

        Args:
            list1 (list): Первый список для сравнения.
            list2 (list): Второй список для сравнения.

        Returns:
            bool: True если списки идентичны, иначе False.
        """
        if len(list1) != len(list2):
            return False
        for item1, item2 in zip(list1, list2):
            if isinstance(item1, list) and isinstance(item2, list):
                if not compare_lists(item1, item2):
                    return False
            elif item1 != item2:
                return False
        return True

    assert compare_lists(actual, expected), msg

current_url()

Возвращает текущий URL страницы.

Returns:

Name Type Description
str str

Текущий URL страницы.

Source code in pages\base_page.py
31
32
33
34
35
36
37
def current_url(self) -> str:
    """Возвращает текущий URL страницы.

    Returns:
        str: Текущий URL страницы.
    """
    return self.page.url

get_api_request_context()

Возвращает контекст API-запросов.

Returns:

Name Type Description
APIRequestContext APIRequestContext

Контекст для выполнения API-запросов.

Source code in pages\base_page.py
62
63
64
65
66
67
68
def get_api_request_context(self) -> APIRequestContext:
    """Возвращает контекст API-запросов.

    Returns:
        APIRequestContext: Контекст для выполнения API-запросов.
    """
    return self.page.context.request

get_response_body(response)

Извлекает тело ответа в format JSON.

Parameters:

Name Type Description Default
response Response

Ответ сервера.

required

Returns:

Type Description
dict | list | None

dict | list | None: Распарсенное тело ответа или None в случае ошибки.

Source code in pages\base_page.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def get_response_body(self, response: Response) -> dict | list | None:
    """Извлекает тело ответа в format JSON.

    Args:
        response (Response): Ответ сервера.

    Returns:
        dict | list | None: Распарсенное тело ответа или None в случае ошибки.
    """
    start_time = time.time()

    # Проверяем что response не None
    if response is None:
        logger.error("Response object is None")
        processing_time = time.time() - start_time
        logger.debug("Response processing time1: %.3f seconds", processing_time)
        return None

    # Проверяем статус ответа
    if response.status >= 400:
        logger.error("API request failed with status %s", response.status)
        processing_time = time.time() - start_time
        logger.debug("Response processing time2: %.3f seconds", processing_time)
        return None

    # Пытаемся получить JSON
    json_result = response.json()

    # Проверяем что результат не None
    if json_result is None:
        logger.error("JSON parsing returned None")
        processing_time = time.time() - start_time
        logger.debug("Response processing time3: %.3f seconds", processing_time)
        return None

    # Принимаем как словари, так и списки
    if not isinstance(json_result, (dict, list)):
        logger.error("Expected dict or list but got %s", type(json_result))
        processing_time = time.time() - start_time
        logger.debug("Response processing time4: %.3f seconds", processing_time)
        return None

    processing_time = time.time() - start_time
    logger.debug("Response processing time5: %.3f seconds", processing_time)

    return json_result

open(uri)

Открывает указанный URI в браузере.

Parameters:

Name Type Description Default
uri str

URI для открытия (без базового URL).

required

Returns:

Type Description
Response | None

Response | None: Ответ сервера или None в случае ошибки.

Source code in pages\base_page.py
39
40
41
42
43
44
45
46
47
48
def open(self, uri) -> Response | None:
    """Открывает указанный URI в браузере.

    Args:
        uri (str): URI для открытия (без базового URL).

    Returns:
        Response | None: Ответ сервера или None в случае ошибки.
    """
    return self.page.goto(f"{host.get_base_url()}{uri}", wait_until='domcontentloaded')

page_reload()

Перезагружает текущую страницу.

Source code in pages\base_page.py
50
51
52
def page_reload(self) -> None:
    """Перезагружает текущую страницу."""
    self.page.reload()

send_get_api_request(uri)

Отправляет GET-запрос к API.

Parameters:

Name Type Description Default
uri str

URI API-эндпоинта (без базового URL).

required

Returns:

Name Type Description
Response Response

Ответ сервера.

Source code in pages\base_page.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def send_get_api_request(self, uri: str) -> Response:
    """Отправляет GET-запрос к API.

    Args:
        uri (str): URI API-эндпоинта (без базового URL).

    Returns:
        Response: Ответ сервера.
    """
    api_request_context = self.get_api_request_context()
    token = host.get_access_token()

    # Проверяем что токен получен
    if not token:
        logger.error("Failed to get access token: token is None or empty")
        # Возвращаем заглушечный response или бросаем исключение
        # В Playwright можно создать mock response если нужно
        return None

    headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
    full_url = f"{host.get_request_url()}{uri}"

    logger.debug("Sending GET request to: %s", full_url)
    response = api_request_context.get(full_url, headers=headers)

    # Логируем статус ответа
    logger.debug("GET response status: %s", response.status)

    return response

send_post_api_request(uri, payload)

Отправляет POST-запрос к API.

Source code in pages\base_page.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def send_post_api_request(self, uri: str, payload: Dict) -> Response:
    """Отправляет POST-запрос к API."""
    api_request_context = self.get_api_request_context()
    token = host.get_access_token()

    if not token:
        logger.error("Failed to get access token: token is None or empty")
        return None

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {token}"
    }
    full_url = f"{host.get_request_url()}{uri}"

    logger.debug("Sending POST request to: %s", full_url)

    # Сериализуем payload в JSON
    json_data = json.dumps(payload)

    # Проверяем что сериализация прошла успешно
    if json_data is None:
        logger.error("Failed to serialize payload to JSON: result is None")
        return None

    if not isinstance(json_data, str):
        logger.error("Failed to serialize payload to JSON: expected string got %s", type(json_data))
        return None

    response = api_request_context.post(
        full_url,
        headers=headers,
        data=json_data  # Передаем сериализованный JSON как data
    )

    logger.debug("POST response status: %s", response.status)

    return response

wait_for_timeout(timeout)

Ожидает указанное количество миллисекунд.

Parameters:

Name Type Description Default
timeout int

Время ожидания в миллисекундах.

required
Source code in pages\base_page.py
54
55
56
57
58
59
60
def wait_for_timeout(self, timeout: int) -> None:
    """Ожидает указанное количество миллисекунд.

    Args:
        timeout (int): Время ожидания в миллисекундах.
    """
    self.page.wait_for_timeout(timeout)