persona-dsl 26.1.21.30__tar.gz → 26.1.21.34__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {persona_dsl-26.1.21.30/src/persona_dsl.egg-info → persona_dsl-26.1.21.34}/PKG-INFO +55 -7
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/README.md +54 -6
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/pyproject.toml +1 -1
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/pytest_plugin.py +196 -30
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34/src/persona_dsl.egg-info}/PKG-INFO +55 -7
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/MANIFEST.in +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/setup.cfg +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/action.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/base_step.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/combined_step.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/expectation.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/fact.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/goal.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/ops.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/step.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/contains_item.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/contains_the_text.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/has_entries.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/is_equal.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/is_greater_than.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/path_equal.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/web/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/web/is_displayed.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/web/matches_aria_snapshot.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/web/matches_screenshot.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/generators/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/generators/api_generator.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/generators/cli.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/generators/page_generator.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/api/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/api/json_as.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/api/json_response.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/api/send_request.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/db/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/db/execute_sql.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/db/fetch_all.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/db/fetch_one.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/kafka/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/kafka/message_in_topic.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/kafka/send_message.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/soap/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/soap/call_operation.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/soap/operation_result.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/aria_snapshot.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/click.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/current_path.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/element_attribute.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/element_is_visible.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/element_text.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/elements_count.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/fill.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/generate_page_object.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/input_value.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/navigate.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/press_key.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/rich_aria_snapshot.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/screenshot.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/table_data.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/wait_for_navigation.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/pages/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/pages/elements.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/pages/page.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/pages/virtual_page.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/persona.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/runtime/dist/persona_bundle.js +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/core/base.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/core/skill_definition.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/use_api.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/use_browser.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/use_database.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/use_kafka.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/use_soap.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/__init__.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/artifacts.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/config.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/data_providers.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/decorators.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/metrics.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/naming.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/path.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/retry.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/taas_integration.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/utils/waits.py +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl.egg-info/SOURCES.txt +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl.egg-info/dependency_links.txt +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl.egg-info/entry_points.txt +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl.egg-info/requires.txt +0 -0
- {persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: persona-dsl
|
|
3
|
-
Version: 26.1.21.
|
|
3
|
+
Version: 26.1.21.34
|
|
4
4
|
Summary: Persona DSL - Framework for implementing Screenplay pattern in Python tests
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -164,15 +164,63 @@ PERSONA_SKILLS = [BaseSkill.BROWSER, BaseSkill.API]
|
|
|
164
164
|
### `config/{env}.yaml`
|
|
165
165
|
Параметры окружения (dev, test, prod). Выбирается через `--env=test`.
|
|
166
166
|
|
|
167
|
+
### Примеры конфигурации
|
|
168
|
+
|
|
169
|
+
Ниже представлен **полный справочник** всех возможных настроек.
|
|
170
|
+
Большинство параметров унифицированы и работают идентично для **Локального** запуска и **Selenoid**.
|
|
171
|
+
|
|
172
|
+
#### Полный справочник (Global Reference)
|
|
173
|
+
|
|
174
|
+
Этот конфиг покрывает все возможности фреймворка. Неиспользуемые параметры можно опускать.
|
|
175
|
+
|
|
167
176
|
```yaml
|
|
168
|
-
|
|
169
|
-
browser:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
skills:
|
|
178
|
+
browser:
|
|
179
|
+
default:
|
|
180
|
+
base_url: "https://my-app.test" # URL должен быть внутри навыка
|
|
181
|
+
|
|
182
|
+
# --- Базовые настройки (Basic) ---
|
|
183
|
+
type: chromium # Тип: chromium, firefox, webkit, sberbrowser, msedge
|
|
184
|
+
headless: true # true = без UI (в контейнере тоже работает)
|
|
185
|
+
channel: chrome # (Optional) Канал: chrome, msedge, chrome-beta
|
|
186
|
+
executable_path: "" # (Optional) Кастомный путь к бинарнику
|
|
187
|
+
version: "128.0" # (Optional) Версия (для Selenoid или скачивания)
|
|
188
|
+
|
|
189
|
+
# --- Размеры и Тайминги (Dimensions & Timings) ---
|
|
190
|
+
viewport:
|
|
191
|
+
width: 1920
|
|
192
|
+
height: 1080 # Всегда используйте многострочный формат для читаемости
|
|
193
|
+
slow_mo: 50.0 # Задержка между действиями (мс)
|
|
194
|
+
default_timeout: 30000.0 # Таймаут поиска элементов (мс)
|
|
195
|
+
default_navigation_timeout: 30000.0 # Таймаут загрузки (мс)
|
|
196
|
+
|
|
197
|
+
# --- Аргументы и Флаги (Arguments) ---
|
|
198
|
+
# Применяются и локально, и удаленно (через goog:chromeOptions)
|
|
199
|
+
no_sandbox: true # --no-sandbox
|
|
200
|
+
ignore_https_errors: true # --ignore-certificate-errors
|
|
201
|
+
disable_features: # --disable-features=...
|
|
202
|
+
- SidePanel
|
|
203
|
+
- SberWhatsNew
|
|
204
|
+
args: # Дополнительные аргументы
|
|
205
|
+
- "--start-maximized"
|
|
206
|
+
- "--disable-notifications"
|
|
207
|
+
- "--disable-gpu"
|
|
208
|
+
|
|
209
|
+
# --- Удаленный запуск (Remote / Selenoid) ---
|
|
210
|
+
# Если URL задан, тесты пойдут в облако/контейнер
|
|
211
|
+
selenoid_url: "http://selenoid.company.com:4444"
|
|
212
|
+
|
|
213
|
+
session_retries: 3 # Повторные попытки при ошибке создания сессии
|
|
214
|
+
session_timeout: 60.0 # Таймаут ожидания слота (сек)
|
|
215
|
+
|
|
216
|
+
selenoid_options:
|
|
217
|
+
enableVNC: true # Включить Live-просмотр
|
|
218
|
+
enableVideo: false # Включить запись видео
|
|
219
|
+
# name/videoName проставляются автоматически
|
|
174
220
|
```
|
|
175
221
|
|
|
222
|
+
|
|
223
|
+
|
|
176
224
|
---
|
|
177
225
|
|
|
178
226
|
## 📊 Отчетность (Allure + TaaS)
|
|
@@ -127,15 +127,63 @@ PERSONA_SKILLS = [BaseSkill.BROWSER, BaseSkill.API]
|
|
|
127
127
|
### `config/{env}.yaml`
|
|
128
128
|
Параметры окружения (dev, test, prod). Выбирается через `--env=test`.
|
|
129
129
|
|
|
130
|
+
### Примеры конфигурации
|
|
131
|
+
|
|
132
|
+
Ниже представлен **полный справочник** всех возможных настроек.
|
|
133
|
+
Большинство параметров унифицированы и работают идентично для **Локального** запуска и **Selenoid**.
|
|
134
|
+
|
|
135
|
+
#### Полный справочник (Global Reference)
|
|
136
|
+
|
|
137
|
+
Этот конфиг покрывает все возможности фреймворка. Неиспользуемые параметры можно опускать.
|
|
138
|
+
|
|
130
139
|
```yaml
|
|
131
|
-
|
|
132
|
-
browser:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
skills:
|
|
141
|
+
browser:
|
|
142
|
+
default:
|
|
143
|
+
base_url: "https://my-app.test" # URL должен быть внутри навыка
|
|
144
|
+
|
|
145
|
+
# --- Базовые настройки (Basic) ---
|
|
146
|
+
type: chromium # Тип: chromium, firefox, webkit, sberbrowser, msedge
|
|
147
|
+
headless: true # true = без UI (в контейнере тоже работает)
|
|
148
|
+
channel: chrome # (Optional) Канал: chrome, msedge, chrome-beta
|
|
149
|
+
executable_path: "" # (Optional) Кастомный путь к бинарнику
|
|
150
|
+
version: "128.0" # (Optional) Версия (для Selenoid или скачивания)
|
|
151
|
+
|
|
152
|
+
# --- Размеры и Тайминги (Dimensions & Timings) ---
|
|
153
|
+
viewport:
|
|
154
|
+
width: 1920
|
|
155
|
+
height: 1080 # Всегда используйте многострочный формат для читаемости
|
|
156
|
+
slow_mo: 50.0 # Задержка между действиями (мс)
|
|
157
|
+
default_timeout: 30000.0 # Таймаут поиска элементов (мс)
|
|
158
|
+
default_navigation_timeout: 30000.0 # Таймаут загрузки (мс)
|
|
159
|
+
|
|
160
|
+
# --- Аргументы и Флаги (Arguments) ---
|
|
161
|
+
# Применяются и локально, и удаленно (через goog:chromeOptions)
|
|
162
|
+
no_sandbox: true # --no-sandbox
|
|
163
|
+
ignore_https_errors: true # --ignore-certificate-errors
|
|
164
|
+
disable_features: # --disable-features=...
|
|
165
|
+
- SidePanel
|
|
166
|
+
- SberWhatsNew
|
|
167
|
+
args: # Дополнительные аргументы
|
|
168
|
+
- "--start-maximized"
|
|
169
|
+
- "--disable-notifications"
|
|
170
|
+
- "--disable-gpu"
|
|
171
|
+
|
|
172
|
+
# --- Удаленный запуск (Remote / Selenoid) ---
|
|
173
|
+
# Если URL задан, тесты пойдут в облако/контейнер
|
|
174
|
+
selenoid_url: "http://selenoid.company.com:4444"
|
|
175
|
+
|
|
176
|
+
session_retries: 3 # Повторные попытки при ошибке создания сессии
|
|
177
|
+
session_timeout: 60.0 # Таймаут ожидания слота (сек)
|
|
178
|
+
|
|
179
|
+
selenoid_options:
|
|
180
|
+
enableVNC: true # Включить Live-просмотр
|
|
181
|
+
enableVideo: false # Включить запись видео
|
|
182
|
+
# name/videoName проставляются автоматически
|
|
137
183
|
```
|
|
138
184
|
|
|
185
|
+
|
|
186
|
+
|
|
139
187
|
---
|
|
140
188
|
|
|
141
189
|
## 📊 Отчетность (Allure + TaaS)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "persona-dsl"
|
|
7
|
-
version = "26.01.21.
|
|
7
|
+
version = "26.01.21.034"
|
|
8
8
|
description = "Persona DSL - Framework for implementing Screenplay pattern in Python tests"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -14,6 +14,7 @@ from urllib.parse import urlparse
|
|
|
14
14
|
import allure
|
|
15
15
|
import pytest
|
|
16
16
|
import yaml
|
|
17
|
+
import requests
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from persona_dsl.components.expectation import Expectation
|
|
@@ -116,6 +117,57 @@ def _get_skill_config(
|
|
|
116
117
|
return named_config if isinstance(named_config, dict) else None
|
|
117
118
|
|
|
118
119
|
|
|
120
|
+
def _create_selenoid_session(
|
|
121
|
+
hub_url: str, capabilities: Dict[str, Any], retries: int = 3, timeout: float = 10.0
|
|
122
|
+
) -> Tuple[str, str]:
|
|
123
|
+
"""
|
|
124
|
+
Создает сессию в Selenoid через WebDriver API с повторными попытками.
|
|
125
|
+
Возвращает кортеж (ws_endpoint, session_id).
|
|
126
|
+
"""
|
|
127
|
+
session_url = f"{hub_url.rstrip('/')}/wd/hub/session"
|
|
128
|
+
last_error: Optional[Exception] = None
|
|
129
|
+
|
|
130
|
+
for attempt in range(retries + 1):
|
|
131
|
+
try:
|
|
132
|
+
logger.info(
|
|
133
|
+
f"Попытка создания сессии Selenoid ({attempt + 1}/{retries + 1})..."
|
|
134
|
+
)
|
|
135
|
+
# requests imported at module level
|
|
136
|
+
resp = requests.post(
|
|
137
|
+
session_url, json={"capabilities": capabilities}, timeout=timeout
|
|
138
|
+
)
|
|
139
|
+
if resp.status_code == 200:
|
|
140
|
+
data = resp.json()
|
|
141
|
+
# Формат ответа WebDriver: { "value": { "sessionId": "...", "capabilities": ... } }
|
|
142
|
+
value = data.get("value", {})
|
|
143
|
+
session_id = value.get("sessionId") or data.get("sessionId")
|
|
144
|
+
|
|
145
|
+
if not session_id:
|
|
146
|
+
raise RuntimeError(f"Selenoid не вернул sessionId: {data}")
|
|
147
|
+
|
|
148
|
+
# Формируем WS URL для CDP
|
|
149
|
+
parsed = urlparse(hub_url)
|
|
150
|
+
ws_scheme = "wss" if parsed.scheme == "https" else "ws"
|
|
151
|
+
ws_endpoint = f"{ws_scheme}://{parsed.netloc}/devtools/{session_id}"
|
|
152
|
+
|
|
153
|
+
logger.info(f"Сессия Selenoid создана: {session_id}")
|
|
154
|
+
return ws_endpoint, session_id
|
|
155
|
+
else:
|
|
156
|
+
logger.warning(
|
|
157
|
+
f"Ошибка создания сессии Selenoid (Code {resp.status_code}): {resp.text}"
|
|
158
|
+
)
|
|
159
|
+
except Exception as e:
|
|
160
|
+
last_error = e
|
|
161
|
+
logger.warning(f"Ошибка подключения к Selenoid: {e}")
|
|
162
|
+
|
|
163
|
+
if attempt < retries:
|
|
164
|
+
time.sleep(2.0)
|
|
165
|
+
|
|
166
|
+
raise RuntimeError(
|
|
167
|
+
f"Не удалось создать сессию Selenoid после {retries + 1} попыток. Последняя ошибка: {last_error}"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
119
171
|
def _get_persona_config(config: Config, role: str) -> Optional[Dict[str, Any]]:
|
|
120
172
|
personas_config = config.personas
|
|
121
173
|
if role == "default":
|
|
@@ -131,6 +183,38 @@ def _get_persona_config(config: Config, role: str) -> Optional[Dict[str, Any]]:
|
|
|
131
183
|
return named_config if isinstance(named_config, dict) else None
|
|
132
184
|
|
|
133
185
|
|
|
186
|
+
def _build_chromium_args(cfg: Dict[str, Any]) -> List[str]:
|
|
187
|
+
"""Строит список аргументов командной строки для Chromium-based браузеров."""
|
|
188
|
+
args = list(cfg.get("args") or [])
|
|
189
|
+
|
|
190
|
+
if cfg.get("no_sandbox", False):
|
|
191
|
+
args.append("--no-sandbox")
|
|
192
|
+
|
|
193
|
+
disable_features = list(cfg.get("disable_features") or [])
|
|
194
|
+
|
|
195
|
+
if cfg.get("ignore_https_errors", False):
|
|
196
|
+
args.append("--ignore-certificate-errors")
|
|
197
|
+
# Отключаем принудительное обновление до HTTPS и связанные с ним предупреждения
|
|
198
|
+
if "HttpsUpgrades" not in disable_features:
|
|
199
|
+
disable_features.append("HttpsUpgrades")
|
|
200
|
+
if "EnforceHttps" not in disable_features:
|
|
201
|
+
disable_features.append("EnforceHttps")
|
|
202
|
+
|
|
203
|
+
# Для HTTP сайтов отключаем предупреждения о небезопасности
|
|
204
|
+
base_url = cfg.get("base_url")
|
|
205
|
+
if base_url:
|
|
206
|
+
parsed_url = urlparse(base_url)
|
|
207
|
+
if parsed_url.scheme == "http":
|
|
208
|
+
origin = f"{parsed_url.scheme}://{parsed_url.netloc}"
|
|
209
|
+
args.append(f"--unsafely-treat-insecure-origin-as-secure={origin}")
|
|
210
|
+
|
|
211
|
+
if disable_features:
|
|
212
|
+
features_str = ",".join(disable_features)
|
|
213
|
+
args.append(f"--disable-features={features_str}")
|
|
214
|
+
|
|
215
|
+
return args
|
|
216
|
+
|
|
217
|
+
|
|
134
218
|
class PersonaDispatcher:
|
|
135
219
|
def __init__(
|
|
136
220
|
self,
|
|
@@ -234,49 +318,116 @@ class PersonaDispatcher:
|
|
|
234
318
|
pw = self._resources["playwright"]
|
|
235
319
|
browser_type = getattr(pw, cfg.get("type", "chromium"))
|
|
236
320
|
|
|
321
|
+
selenoid_url = cfg.get("selenoid_url")
|
|
237
322
|
ws_endpoint = cfg.get("ws_endpoint")
|
|
238
323
|
|
|
239
|
-
if
|
|
324
|
+
if selenoid_url:
|
|
325
|
+
# Создание сессии в Selenoid
|
|
326
|
+
selenoid_opts = cfg.get("selenoid_options", {})
|
|
327
|
+
|
|
328
|
+
# Определяем browserName для Selenoid
|
|
329
|
+
requested_type = cfg.get("type", "chromium")
|
|
330
|
+
if requested_type == "sberbrowser":
|
|
331
|
+
browser_name = "sberbrowser"
|
|
332
|
+
# Playwright не знает про sberbrowser, используем chromium
|
|
333
|
+
browser_type = getattr(pw, "chromium")
|
|
334
|
+
elif requested_type == "chromium":
|
|
335
|
+
browser_name = "chrome" # Selenoid обычно использует "chrome" или "chromium"
|
|
336
|
+
else:
|
|
337
|
+
browser_name = requested_type
|
|
338
|
+
|
|
339
|
+
# Базовые capabilities
|
|
340
|
+
caps = {
|
|
341
|
+
"browserName": browser_name,
|
|
342
|
+
"version": cfg.get("version", ""),
|
|
343
|
+
"enableVNC": selenoid_opts.get("enableVNC", True),
|
|
344
|
+
"enableVideo": selenoid_opts.get("enableVideo", False),
|
|
345
|
+
}
|
|
346
|
+
# Добавляем специфичные опции
|
|
347
|
+
if "goog:chromeOptions" in selenoid_opts:
|
|
348
|
+
caps["goog:chromeOptions"] = selenoid_opts["goog:chromeOptions"]
|
|
349
|
+
|
|
350
|
+
# Другие опции первого уровня
|
|
351
|
+
for k, v in selenoid_opts.items():
|
|
352
|
+
if k not in caps:
|
|
353
|
+
caps[k] = v
|
|
354
|
+
|
|
355
|
+
# --- UNIFICATION: Inject config args into capabilities ---
|
|
356
|
+
common_args = _build_chromium_args(cfg)
|
|
357
|
+
|
|
358
|
+
opts_key = "goog:chromeOptions"
|
|
359
|
+
if "browserName" in caps and "firefox" in caps["browserName"]:
|
|
360
|
+
opts_key = "moz:firefoxOptions"
|
|
361
|
+
|
|
362
|
+
if opts_key not in caps:
|
|
363
|
+
caps[opts_key] = {}
|
|
364
|
+
if "args" not in caps[opts_key]:
|
|
365
|
+
caps[opts_key]["args"] = []
|
|
366
|
+
|
|
367
|
+
# 1. Headless (from config)
|
|
368
|
+
if cfg.get("headless", False):
|
|
369
|
+
if "--headless" not in caps[opts_key]["args"]:
|
|
370
|
+
caps[opts_key]["args"].append("--headless")
|
|
371
|
+
|
|
372
|
+
# 2. Common Args (no_sandbox, disable_features, etc)
|
|
373
|
+
# Avoid duplicates
|
|
374
|
+
existing_args = set(caps[opts_key]["args"])
|
|
375
|
+
for arg in common_args:
|
|
376
|
+
if arg not in existing_args:
|
|
377
|
+
caps[opts_key]["args"].append(arg)
|
|
378
|
+
|
|
379
|
+
# 3. Добавление имени теста
|
|
380
|
+
if self.request and self.request.node:
|
|
381
|
+
caps["name"] = self.request.node.name
|
|
382
|
+
if caps.get("enableVideo"):
|
|
383
|
+
caps["videoName"] = f"{self.request.node.name}.mp4"
|
|
384
|
+
|
|
385
|
+
# Все capabilities оборачиваем в alwaysMatch для W3C
|
|
386
|
+
w3c_caps = {"alwaysMatch": caps}
|
|
387
|
+
|
|
388
|
+
retries = cfg.get("session_retries", 3)
|
|
389
|
+
timeout = cfg.get("session_timeout", 30.0)
|
|
390
|
+
|
|
391
|
+
ws_url, session_id = _create_selenoid_session(
|
|
392
|
+
selenoid_url, w3c_caps, retries=retries, timeout=timeout
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
# Сохраняем для очистки
|
|
396
|
+
if "selenoid_sessions" not in self._resources:
|
|
397
|
+
self._resources["selenoid_sessions"] = []
|
|
398
|
+
self._resources["selenoid_sessions"].append(
|
|
399
|
+
{"url": selenoid_url, "id": session_id}
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
self._resources["browser"] = browser_type.connect_over_cdp(
|
|
403
|
+
ws_url,
|
|
404
|
+
slow_mo=float(cfg.get("slow_mo", 0.0)),
|
|
405
|
+
timeout=float(cfg.get("timeout", 30000.0)),
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
elif ws_endpoint:
|
|
240
409
|
# Подключение к удалённому браузеру по WebSocket
|
|
241
410
|
self._resources["browser"] = browser_type.connect_over_cdp(
|
|
242
|
-
ws_endpoint
|
|
411
|
+
ws_endpoint,
|
|
412
|
+
slow_mo=float(cfg.get("slow_mo", 0.0)),
|
|
413
|
+
timeout=float(cfg.get("timeout", 30000.0)),
|
|
243
414
|
)
|
|
244
415
|
else:
|
|
245
416
|
# Локальный запуск
|
|
246
|
-
args = cfg
|
|
247
|
-
if cfg.get("no_sandbox", False):
|
|
248
|
-
args.append("--no-sandbox")
|
|
249
|
-
|
|
250
|
-
disable_features = cfg.get("disable_features") or []
|
|
251
|
-
|
|
252
|
-
if cfg.get("ignore_https_errors", False):
|
|
253
|
-
args.append("--ignore-certificate-errors")
|
|
254
|
-
# Отключаем принудительное обновление до HTTPS и связанные с ним предупреждения
|
|
255
|
-
if "HttpsUpgrades" not in disable_features:
|
|
256
|
-
disable_features.append("HttpsUpgrades")
|
|
257
|
-
if "EnforceHttps" not in disable_features:
|
|
258
|
-
disable_features.append("EnforceHttps")
|
|
259
|
-
|
|
260
|
-
# Для HTTP сайтов отключаем предупреждения о небезопасности
|
|
261
|
-
base_url = cfg.get("base_url")
|
|
262
|
-
if base_url:
|
|
263
|
-
parsed_url = urlparse(base_url)
|
|
264
|
-
if parsed_url.scheme == "http":
|
|
265
|
-
origin = f"{parsed_url.scheme}://{parsed_url.netloc}"
|
|
266
|
-
args.append(
|
|
267
|
-
f"--unsafely-treat-insecure-origin-as-secure={origin}"
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
if disable_features:
|
|
271
|
-
features_str = ",".join(disable_features)
|
|
272
|
-
args.append(f"--disable-features={features_str}")
|
|
417
|
+
args = _build_chromium_args(cfg)
|
|
273
418
|
|
|
274
419
|
executable_path = cfg.get("executable_path")
|
|
420
|
+
channel = cfg.get("channel")
|
|
421
|
+
slow_mo = float(cfg.get("slow_mo", 0.0))
|
|
422
|
+
timeout = float(cfg.get("timeout", 30000.0))
|
|
275
423
|
|
|
276
424
|
launch_opts = {
|
|
277
425
|
"headless": cfg.get("headless", True),
|
|
278
426
|
"executable_path": executable_path,
|
|
279
427
|
"args": args if args else None,
|
|
428
|
+
"channel": channel,
|
|
429
|
+
"slow_mo": slow_mo,
|
|
430
|
+
"timeout": timeout,
|
|
280
431
|
}
|
|
281
432
|
launch_opts = {
|
|
282
433
|
k: v for k, v in launch_opts.items() if v is not None
|
|
@@ -507,7 +658,22 @@ class PersonaDispatcher:
|
|
|
507
658
|
for context in self._resources["browser_contexts"].values():
|
|
508
659
|
context.close()
|
|
509
660
|
if "browser" in self._resources:
|
|
510
|
-
|
|
661
|
+
try:
|
|
662
|
+
self._resources["browser"].close()
|
|
663
|
+
except Exception as e:
|
|
664
|
+
logger.warning(f"Ошибка при закрытии браузера: {e}")
|
|
665
|
+
|
|
666
|
+
# Удаление сессий Selenoid
|
|
667
|
+
if "selenoid_sessions" in self._resources:
|
|
668
|
+
for session in self._resources["selenoid_sessions"]:
|
|
669
|
+
s_url = session["url"]
|
|
670
|
+
s_id = session["id"]
|
|
671
|
+
try:
|
|
672
|
+
del_url = f"{s_url.rstrip('/')}/wd/hub/session/{s_id}"
|
|
673
|
+
logger.info(f"Удаление сессии Selenoid: {s_id}")
|
|
674
|
+
requests.delete(del_url, timeout=5.0)
|
|
675
|
+
except Exception as e:
|
|
676
|
+
logger.warning(f"Ошибка удаления сессии Selenoid {s_id}: {e}")
|
|
511
677
|
if "playwright" in self._resources:
|
|
512
678
|
pw = self._resources["playwright"]
|
|
513
679
|
if not hasattr(pw, "stop") or not callable(pw.stop):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: persona-dsl
|
|
3
|
-
Version: 26.1.21.
|
|
3
|
+
Version: 26.1.21.34
|
|
4
4
|
Summary: Persona DSL - Framework for implementing Screenplay pattern in Python tests
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -164,15 +164,63 @@ PERSONA_SKILLS = [BaseSkill.BROWSER, BaseSkill.API]
|
|
|
164
164
|
### `config/{env}.yaml`
|
|
165
165
|
Параметры окружения (dev, test, prod). Выбирается через `--env=test`.
|
|
166
166
|
|
|
167
|
+
### Примеры конфигурации
|
|
168
|
+
|
|
169
|
+
Ниже представлен **полный справочник** всех возможных настроек.
|
|
170
|
+
Большинство параметров унифицированы и работают идентично для **Локального** запуска и **Selenoid**.
|
|
171
|
+
|
|
172
|
+
#### Полный справочник (Global Reference)
|
|
173
|
+
|
|
174
|
+
Этот конфиг покрывает все возможности фреймворка. Неиспользуемые параметры можно опускать.
|
|
175
|
+
|
|
167
176
|
```yaml
|
|
168
|
-
|
|
169
|
-
browser:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
skills:
|
|
178
|
+
browser:
|
|
179
|
+
default:
|
|
180
|
+
base_url: "https://my-app.test" # URL должен быть внутри навыка
|
|
181
|
+
|
|
182
|
+
# --- Базовые настройки (Basic) ---
|
|
183
|
+
type: chromium # Тип: chromium, firefox, webkit, sberbrowser, msedge
|
|
184
|
+
headless: true # true = без UI (в контейнере тоже работает)
|
|
185
|
+
channel: chrome # (Optional) Канал: chrome, msedge, chrome-beta
|
|
186
|
+
executable_path: "" # (Optional) Кастомный путь к бинарнику
|
|
187
|
+
version: "128.0" # (Optional) Версия (для Selenoid или скачивания)
|
|
188
|
+
|
|
189
|
+
# --- Размеры и Тайминги (Dimensions & Timings) ---
|
|
190
|
+
viewport:
|
|
191
|
+
width: 1920
|
|
192
|
+
height: 1080 # Всегда используйте многострочный формат для читаемости
|
|
193
|
+
slow_mo: 50.0 # Задержка между действиями (мс)
|
|
194
|
+
default_timeout: 30000.0 # Таймаут поиска элементов (мс)
|
|
195
|
+
default_navigation_timeout: 30000.0 # Таймаут загрузки (мс)
|
|
196
|
+
|
|
197
|
+
# --- Аргументы и Флаги (Arguments) ---
|
|
198
|
+
# Применяются и локально, и удаленно (через goog:chromeOptions)
|
|
199
|
+
no_sandbox: true # --no-sandbox
|
|
200
|
+
ignore_https_errors: true # --ignore-certificate-errors
|
|
201
|
+
disable_features: # --disable-features=...
|
|
202
|
+
- SidePanel
|
|
203
|
+
- SberWhatsNew
|
|
204
|
+
args: # Дополнительные аргументы
|
|
205
|
+
- "--start-maximized"
|
|
206
|
+
- "--disable-notifications"
|
|
207
|
+
- "--disable-gpu"
|
|
208
|
+
|
|
209
|
+
# --- Удаленный запуск (Remote / Selenoid) ---
|
|
210
|
+
# Если URL задан, тесты пойдут в облако/контейнер
|
|
211
|
+
selenoid_url: "http://selenoid.company.com:4444"
|
|
212
|
+
|
|
213
|
+
session_retries: 3 # Повторные попытки при ошибке создания сессии
|
|
214
|
+
session_timeout: 60.0 # Таймаут ожидания слота (сек)
|
|
215
|
+
|
|
216
|
+
selenoid_options:
|
|
217
|
+
enableVNC: true # Включить Live-просмотр
|
|
218
|
+
enableVideo: false # Включить запись видео
|
|
219
|
+
# name/videoName проставляются автоматически
|
|
174
220
|
```
|
|
175
221
|
|
|
222
|
+
|
|
223
|
+
|
|
176
224
|
---
|
|
177
225
|
|
|
178
226
|
## 📊 Отчетность (Allure + TaaS)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/components/combined_step.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/is_equal.py
RENAMED
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/generic/path_equal.py
RENAMED
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/web/__init__.py
RENAMED
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/expectations/web/is_displayed.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/generators/api_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/generators/page_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/kafka/message_in_topic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/soap/call_operation.py
RENAMED
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/soap/operation_result.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/element_attribute.py
RENAMED
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/element_is_visible.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/generate_page_object.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/rich_aria_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/ops/web/wait_for_navigation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/runtime/dist/persona_bundle.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl/skills/core/skill_definition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persona_dsl-26.1.21.30 → persona_dsl-26.1.21.34}/src/persona_dsl.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|