Быстрый фреймворк для UI-тестирования на WebSocker
Project description
Документация библиотеки PuzzelUITest
Общее описание
PuzzelUITest - это библиотека для тестирования веб-интерфейсов, построенная на основе Playwright. Она предоставляет удобный DSL (Domain-Specific Language) для описания структуры веб-страниц и автоматизации тестирования.
Ключевые особенности:
- Объектная модель страниц - описывайте страницы как набор объектов
- Автоматизация тестов - встроенная поддержка параметризованных тестов
- Типизация - полная поддержка типов для IDE
- Интеграция с Playwright - используйте всю мощь Playwright
📦 Установка
pip install puzzeluitest / uv add puzzeluitest
Зависимости
- Python 3.7+
- playwright
- pytest
Основные компоненты
1. Базовый класс Object
Базовый класс для всех элементов страницы.
class Object:
"""
Базовый класс для всех элементов страницы.
Args:
locator: CSS/XPath селектор элемента
"""
def __init__(self, locator: str):
self.locator = locator
2. Класс Input
Класс для описания полей ввода.
class Input(Object):
"""
Класс для описания поля ввода.
Args:
locator: CSS/XPath селектор поля ввода
type_: Тип поля (login, password, email)
true_data: Корректные данные для этого поля (опционально)
Example:
>>> username = Input('#username', type_='login', true_data='admin')
>>> password = Input('#password', type_='password', true_data='pass123')
"""
def __init__(self, locator: str, type_: Literal["login", "password", "email"], true_data: str = ""):
super().__init__(locator)
self.type = type_
self.true_data = true_data
3. Класс Button
Класс для описания кнопок.
class Button(Object):
"""
Класс для описания кнопки.
Args:
locator: CSS/XPath селектор кнопки
Example:
>>> submit = Button('#submit-btn')
>>> cancel = Button('button:has-text("Cancel")')
"""
def __init__(self, locator: str):
super().__init__(locator)
4. Класс Link
Класс для описания ссылок.
class Link(Object):
"""
Класс для описания ссылки.
Args:
locator: CSS/XPath селектор ссылки
Example:
>>> forgot_password = Link('#forgot-password')
>>> home = Link('a:has-text("Home")')
"""
def __init__(self, locator: str):
super().__init__(locator)
5. Класс Auth (основной)
Базовый класс для тестирования страниц.
class Auth:
"""
Базовый класс для тестирования страниц.
Атрибуты класса:
url: URL страницы
delay_in: Задержка перед действием (секунды)
delay_out: Задержка после действия (секунды)
input_data: Словарь с тестовыми данными
Опциональные атрибуты:
click_out: Локатор для клика перед заполнением
click_in: Локатор для клика после заполнения
fill_out: Локатор для заполнения перед основными полями
fill_in: Локатор для заполнения после основных полей
get_text_out: Локатор для получения текста перед действиями
get_text_in: Локатор для получения текста после действий
Example:
>>> class LoginPage(Auth):
... url = 'https://example.com/login'
... delay_in = 1
... delay_out = 2
... login = Input('#username', type_='login')
... password = Input('#password', type_='password')
... button = Button('#login-btn')
"""
Примеры использования
Пример 1: Простая страница авторизации
from puzzeluitest.web import Auth, Input, Button, Page
class LoginPage(Auth):
url = 'https://example.com/login'
delay_in = 1
delay_out = 1
# Элементы страницы
username = Input('#username', type_='login', true_data='testuser')
password = Input('#password', type_='password', true_data='TestPass123!')
login_button = Button('button[type="submit"]')
# Дополнительные элементы
forgot_password = Link('#forgot-password')
register_link = Link('a:has-text("Register")')
def test_login(page: Page):
login_page = LoginPage(page)
login_page.run_tests() # Запуск всех тестов из INPUT_DATA
Пример 2: Сложная страница с модальным окном
from puzzeluitest.web import Auth, Input, Button, Page
class DemoblazeSignUp(Auth):
url = 'https://www.demoblaze.com/'
delay_in = 1
delay_out = 2
# Шаг 1: Открытие модального окна
open_signup_btn = '#signin2' # click_out
# Шаг 2: Поля в модальном окне
username = Input('#sign-username', type_='login', true_data='testuser123')
password = Input('#sign-password', type_='password', true_data='TestPass123!')
# Шаг 3: Кнопка отправки
signup_btn = Button('button:has-text("Sign up")')
def run_tests(self):
"""Кастомная реализация для demoblaze"""
for test_data in self.input_data.values():
# Открыть форму
self.page.click(self.open_signup_btn)
time.sleep(self.delay_in)
# Заполнить поля
self.page.fill(self.username.locator, test_data.get('login', ''))
self.page.fill(self.password.locator, test_data.get('password', ''))
# Отправить форму
self.page.click(self.signup_btn.locator)
# Обработать alert
self.page.on("dialog", lambda dialog: dialog.accept())
time.sleep(self.delay_out)
# Перезагрузить страницу
self.page.goto(self.url)
def test_signup(page: Page):
signup_page = DemoblazeSignUp(page)
signup_page.run_tests()
Пример 3: Многостраничный тест
from puzzeluitest.web import Auth, Input, Button, Page
class MainPage(Auth):
url = 'https://example.com/'
login_link = Link('a:has-text("Login")')
register_link = Link('a:has-text("Register")')
class LoginPage(Auth):
url = 'https://example.com/login'
username = Input('#username', type_='login')
password = Input('#password', type_='password')
submit = Button('#submit')
class ProfilePage(Auth):
url = 'https://example.com/profile'
username_display = '#username-display'
logout_btn = Button('#logout')
def test_full_flow(page: Page):
# Переход на главную
main = MainPage(page)
main.run_test()
main.page.click(main.login_link.locator)
# Авторизация
login = LoginPage(page)
login.page.wait_for_url(login.url)
login.fill(login.username.locator, 'testuser')
login.fill(login.password.locator, 'password')
login.click(login.submit.locator)
# Проверка профиля
profile = ProfilePage(page)
profile.page.wait_for_url(profile.url)
assert profile.page.locator(profile.username_display).is_visible()
Тестовые данные
Библиотека включает встроенный набор тестовых данных:
INPUT_DATA_LOGIN = {
"valid": {"login": "testuser", "password": "Pass123!", "email": "test@example.com"},
"empty": {"login": "", "password": "", "email": ""},
"spaces": {"login": " ", "password": " ", "email": " @test.com"},
"sql_injection": {"login": "' OR '1'='1", "password": "pass", "email": "test@test.com"},
"xss": {"login": "<script>alert(1)</script>", "password": "pass", "email": "test@test.com"},
# ... и многие другие
}
🔧 Расширение библиотеки
Создание собственных классов элементов
from puzzeluitest.web import Object
class Checkbox(Object):
"""Кастомный класс для чекбоксов"""
def __init__(self, locator: str, checked_by_default: bool = False):
super().__init__(locator)
self.checked_by_default = checked_by_default
def is_checked(self, page):
return page.locator(self.locator).is_checked()
class Dropdown(Object):
"""Кастомный класс для выпадающих списков"""
def __init__(self, locator: str, options: list):
super().__init__(locator)
self.options = options
def select_option(self, page, value):
page.select_option(self.locator, value)
Создание собственных методов в странице
class CustomPage(Auth):
url = 'https://example.com'
search_input = Input('#search', type_='login')
search_btn = Button('#search-btn')
results = '#results'
def search(self, query: str):
"""Кастомный метод поиска"""
self.fill(self.search_input.locator, query)
self.click(self.search_btn.locator)
return self.get_text(self.results)
def wait_for_results(self):
"""Ожидание результатов"""
self.page.wait_for_selector(self.results, state='visible')
Лучшие практики
1. Выбор локаторов
# Плохо (зависит от классов)
button = Button('.btn-primary')
# Хорошо (по тексту)
button = Button('button:has-text("Submit")')
# Лучше всего (по data-атрибуту)
button = Button('[data-testid="submit-button"]')
2. Организация тестов
# test_login.py
import pytest
from pages.login_page import LoginPage
def test_valid_login(page):
login_page = LoginPage(page)
login_page.run_test('valid')
assert login_page.page.url == 'https://example.com/dashboard'
def test_invalid_login(page):
login_page = LoginPage(page)
login_page.run_test('sql_injection')
assert login_page.page.locator('.error').is_visible()
3. Использование фикстур
# conftest.py
import pytest
from playwright.sync_api import sync_playwright
@pytest.fixture
def page():
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
yield page
browser.close()
📋 Требования к окружению
- Python 3.7 или выше
- Playwright (
pip install playwright) - Браузеры (
playwright install)
🐛 Отладка
Включение логов
import logging
logging.basicConfig(level=logging.DEBUG)
Скриншоты при ошибках
class DebugPage(Auth):
def run_tests(self):
try:
super().run_tests()
except Exception as e:
self.page.screenshot(path='error.png')
raise e
📄 Лицензия
MIT License
🤝 Вклад в развитие
- Fork репозитория
- Создайте ветку (
git checkout -b feature/amazing-feature) - Commit изменений (
git commit -m 'Add amazing feature') - Push в ветку (
git push origin feature/amazing-feature) - Откройте Pull Request
Поддержка
- Психологическая поддержка https://msph.mos.ru/
- Документация: https://stellar-canoe-bf0.notion.site/puzzeluitest-32651859ba3580f49655c072622ae0fe?source=copy_link
Заключение
PuzzelUITest предоставляет удобный и гибкий способ описания веб-страниц и автоматизации тестирования. Благодаря объектной модели и встроенным тестовым данным, вы можете быстро создавать надежные и поддерживаемые тесты.
Начните с простого:
from puzzeluitest.web import Auth, Input, Button, Page
class MyPage(Auth):
url = 'your-url'
# определите элементы
def test_my_page(page: Page):
my_page = MyPage(page)
my_page.run_tests()
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file puzzeluitest-0.1.4.tar.gz.
File metadata
- Download URL: puzzeluitest-0.1.4.tar.gz
- Upload date:
- Size: 24.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd4bb624b51780c8f109c0392a0a8a0d0ba1159ac46d6e4fe4e598018fc163ba
|
|
| MD5 |
d892a27fe65827b2f75ef027136694b9
|
|
| BLAKE2b-256 |
7a900b13af85312996a27dbe709554bdeedf61a79a5dde6d0232fdba469a8407
|
File details
Details for the file puzzeluitest-0.1.4-py3-none-any.whl.
File metadata
- Download URL: puzzeluitest-0.1.4-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
501f4b11dcffcaa482f7595d4932d869c233c6eb135b293f44247440361aba6b
|
|
| MD5 |
97e882b7017f74657b8f502994ddc2a0
|
|
| BLAKE2b-256 |
fa964f30dd99e88dc98143d7c91f05042bda001090a3cbee52caa66e60bb3b23
|