Metadata-Version: 2.4
Name: puzzeluitest
Version: 0.1.4
Summary: Быстрый фреймворк для UI-тестирования на WebSocker
Project-URL: Homepage, https://github.com/zirael/puzzeluitest
Project-URL: Source Code, https://github.com/teex124/puzzeluitest
Project-URL: Bug Tracker, https://github.com/teex124/puzzeluitest/issues
Project-URL: Docs, https://stellar-canoe-bf0.notion.site/puzzeluitest-32651859ba3580f49655c072622ae0fe?source=copy_link
Author-email: Асламов Данил <danil.aslamov@list.ru>
License: MIT
Classifier: Framework :: Pytest
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.13
Requires-Dist: playwright>=1.48.0
Requires-Dist: pytest-playwright>=0.7.2
Requires-Dist: pytest>=9.0.2
Description-Content-Type: text/markdown

# Документация библиотеки PuzzelUITest

## Общее описание

**PuzzelUITest** - это библиотека для тестирования веб-интерфейсов, построенная на основе Playwright. Она предоставляет удобный DSL (Domain-Specific Language) для описания структуры веб-страниц и автоматизации тестирования.

### Ключевые особенности:
-  **Объектная модель страниц** - описывайте страницы как набор объектов
-  **Автоматизация тестов** - встроенная поддержка параметризованных тестов
-  **Типизация** - полная поддержка типов для IDE
-  **Интеграция с Playwright** - используйте всю мощь Playwright

---

## 📦 Установка

```bash
pip install puzzeluitest / uv add puzzeluitest
```

### Зависимости
- Python 3.7+
- playwright
- pytest

---

##  Основные компоненты

### 1. Базовый класс `Object`

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

```python
class Object:
    """
    Базовый класс для всех элементов страницы.
    
    Args:
        locator: CSS/XPath селектор элемента
    """
    def __init__(self, locator: str):
        self.locator = locator
```

### 2. Класс `Input`

Класс для описания полей ввода.

```python
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`

Класс для описания кнопок.

```python
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`

Класс для описания ссылок.

```python
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` (основной)

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

```python
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: Простая страница авторизации

```python
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: Сложная страница с модальным окном

```python
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: Многостраничный тест

```python
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()
```

---

## Тестовые данные

Библиотека включает встроенный набор тестовых данных:

```python
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"},
    # ... и многие другие
}
```

---

## 🔧 Расширение библиотеки

### Создание собственных классов элементов

```python
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)
```

### Создание собственных методов в странице

```python
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. Выбор локаторов

```python
# Плохо (зависит от классов)
button = Button('.btn-primary')

# Хорошо (по тексту)
button = Button('button:has-text("Submit")')

# Лучше всего (по data-атрибуту)
button = Button('[data-testid="submit-button"]')
```

### 2. Организация тестов

```python
# 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. Использование фикстур

```python
# 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`)

---

## 🐛 Отладка

### Включение логов

```python
import logging
logging.basicConfig(level=logging.DEBUG)
```

### Скриншоты при ошибках

```python
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

---

## Поддержка
- Психологическая поддержка https://msph.mos.ru/
- Документация: https://stellar-canoe-bf0.notion.site/puzzeluitest-32651859ba3580f49655c072622ae0fe?source=copy_link


---

##  Заключение

**PuzzelUITest** предоставляет удобный и гибкий способ описания веб-страниц и автоматизации тестирования. Благодаря объектной модели и встроенным тестовым данным, вы можете быстро создавать надежные и поддерживаемые тесты.

Начните с простого:
```python
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()
```