Skip to main content

Быстрый фреймворк для 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


🤝 Вклад в развитие

  1. Fork репозитория
  2. Создайте ветку (git checkout -b feature/amazing-feature)
  3. Commit изменений (git commit -m 'Add amazing feature')
  4. Push в ветку (git push origin feature/amazing-feature)
  5. Откройте Pull Request

Поддержка


Заключение

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

puzzeluitest-0.1.4.tar.gz (24.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

puzzeluitest-0.1.4-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

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

Hashes for puzzeluitest-0.1.4.tar.gz
Algorithm Hash digest
SHA256 cd4bb624b51780c8f109c0392a0a8a0d0ba1159ac46d6e4fe4e598018fc163ba
MD5 d892a27fe65827b2f75ef027136694b9
BLAKE2b-256 7a900b13af85312996a27dbe709554bdeedf61a79a5dde6d0232fdba469a8407

See more details on using hashes here.

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

Hashes for puzzeluitest-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 501f4b11dcffcaa482f7595d4932d869c233c6eb135b293f44247440361aba6b
MD5 97e882b7017f74657b8f502994ddc2a0
BLAKE2b-256 fa964f30dd99e88dc98143d7c91f05042bda001090a3cbee52caa66e60bb3b23

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page