Update.
This commit is contained in:
parent
b25fb89df4
commit
3f80a551d5
@ -1,28 +1,15 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import pytest_asyncio
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient, ASGITransport
|
||||||
from fastapi import status
|
from fastapi import status
|
||||||
from tortoise.contrib.test import initializer, finalizer
|
|
||||||
from mywebdav.main import app
|
from mywebdav.main import app
|
||||||
from mywebdav.models import User
|
from mywebdav.models import User
|
||||||
from mywebdav.billing.models import PricingConfig, Invoice, UsageAggregate, UserSubscription
|
from mywebdav.billing.models import PricingConfig, Invoice, UsageAggregate, UserSubscription
|
||||||
from mywebdav.auth import create_access_token
|
from mywebdav.auth import create_access_token
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest_asyncio.fixture
|
||||||
def event_loop():
|
|
||||||
import asyncio
|
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
|
||||||
yield loop
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", autouse=True)
|
|
||||||
async def initialize_tests():
|
|
||||||
initializer(["mywebdav.models", "mywebdav.billing.models"], db_url="sqlite://:memory:")
|
|
||||||
yield
|
|
||||||
await finalizer()
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
async def test_user():
|
async def test_user():
|
||||||
user = await User.create(
|
user = await User.create(
|
||||||
username="testuser",
|
username="testuser",
|
||||||
@ -34,7 +21,7 @@ async def test_user():
|
|||||||
yield user
|
yield user
|
||||||
await user.delete()
|
await user.delete()
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest_asyncio.fixture
|
||||||
async def admin_user():
|
async def admin_user():
|
||||||
user = await User.create(
|
user = await User.create(
|
||||||
username="adminuser",
|
username="adminuser",
|
||||||
@ -46,17 +33,17 @@ async def admin_user():
|
|||||||
yield user
|
yield user
|
||||||
await user.delete()
|
await user.delete()
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest_asyncio.fixture
|
||||||
async def auth_token(test_user):
|
async def auth_token(test_user):
|
||||||
token = create_access_token(data={"sub": test_user.username})
|
token = create_access_token(data={"sub": test_user.username})
|
||||||
return token
|
return token
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest_asyncio.fixture
|
||||||
async def admin_token(admin_user):
|
async def admin_token(admin_user):
|
||||||
token = create_access_token(data={"sub": admin_user.username})
|
token = create_access_token(data={"sub": admin_user.username})
|
||||||
return token
|
return token
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest_asyncio.fixture
|
||||||
async def pricing_config():
|
async def pricing_config():
|
||||||
configs = []
|
configs = []
|
||||||
configs.append(await PricingConfig.create(
|
configs.append(await PricingConfig.create(
|
||||||
@ -89,7 +76,7 @@ async def pricing_config():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_current_usage(test_user, auth_token):
|
async def test_get_current_usage(test_user, auth_token):
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
"/api/billing/usage/current",
|
"/api/billing/usage/current",
|
||||||
headers={"Authorization": f"Bearer {auth_token}"}
|
headers={"Authorization": f"Bearer {auth_token}"}
|
||||||
@ -113,7 +100,7 @@ async def test_get_monthly_usage(test_user, auth_token):
|
|||||||
bandwidth_down_bytes=1024 ** 3 * 5
|
bandwidth_down_bytes=1024 ** 3 * 5
|
||||||
)
|
)
|
||||||
|
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
f"/api/billing/usage/monthly?year={today.year}&month={today.month}",
|
f"/api/billing/usage/monthly?year={today.year}&month={today.month}",
|
||||||
headers={"Authorization": f"Bearer {auth_token}"}
|
headers={"Authorization": f"Bearer {auth_token}"}
|
||||||
@ -127,7 +114,7 @@ async def test_get_monthly_usage(test_user, auth_token):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_subscription(test_user, auth_token):
|
async def test_get_subscription(test_user, auth_token):
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
"/api/billing/subscription",
|
"/api/billing/subscription",
|
||||||
headers={"Authorization": f"Bearer {auth_token}"}
|
headers={"Authorization": f"Bearer {auth_token}"}
|
||||||
@ -153,7 +140,7 @@ async def test_list_invoices(test_user, auth_token):
|
|||||||
status="open"
|
status="open"
|
||||||
)
|
)
|
||||||
|
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
"/api/billing/invoices",
|
"/api/billing/invoices",
|
||||||
headers={"Authorization": f"Bearer {auth_token}"}
|
headers={"Authorization": f"Bearer {auth_token}"}
|
||||||
@ -179,7 +166,7 @@ async def test_get_invoice(test_user, auth_token):
|
|||||||
status="open"
|
status="open"
|
||||||
)
|
)
|
||||||
|
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
f"/api/billing/invoices/{invoice.id}",
|
f"/api/billing/invoices/{invoice.id}",
|
||||||
headers={"Authorization": f"Bearer {auth_token}"}
|
headers={"Authorization": f"Bearer {auth_token}"}
|
||||||
@ -193,7 +180,7 @@ async def test_get_invoice(test_user, auth_token):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_pricing():
|
async def test_get_pricing():
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get("/api/billing/pricing")
|
response = await client.get("/api/billing/pricing")
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
@ -202,7 +189,7 @@ async def test_get_pricing():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_admin_get_pricing(admin_user, admin_token, pricing_config):
|
async def test_admin_get_pricing(admin_user, admin_token, pricing_config):
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
"/api/admin/billing/pricing",
|
"/api/admin/billing/pricing",
|
||||||
headers={"Authorization": f"Bearer {admin_token}"}
|
headers={"Authorization": f"Bearer {admin_token}"}
|
||||||
@ -216,7 +203,7 @@ async def test_admin_get_pricing(admin_user, admin_token, pricing_config):
|
|||||||
async def test_admin_update_pricing(admin_user, admin_token, pricing_config):
|
async def test_admin_update_pricing(admin_user, admin_token, pricing_config):
|
||||||
config_id = pricing_config[0].id
|
config_id = pricing_config[0].id
|
||||||
|
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.put(
|
response = await client.put(
|
||||||
f"/api/admin/billing/pricing/{config_id}",
|
f"/api/admin/billing/pricing/{config_id}",
|
||||||
headers={"Authorization": f"Bearer {admin_token}"},
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||||||
@ -233,7 +220,7 @@ async def test_admin_update_pricing(admin_user, admin_token, pricing_config):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_admin_get_stats(admin_user, admin_token):
|
async def test_admin_get_stats(admin_user, admin_token):
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
"/api/admin/billing/stats",
|
"/api/admin/billing/stats",
|
||||||
headers={"Authorization": f"Bearer {admin_token}"}
|
headers={"Authorization": f"Bearer {admin_token}"}
|
||||||
@ -247,7 +234,7 @@ async def test_admin_get_stats(admin_user, admin_token):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_non_admin_cannot_access_admin_endpoints(test_user, auth_token):
|
async def test_non_admin_cannot_access_admin_endpoints(test_user, auth_token):
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
"/api/admin/billing/pricing",
|
"/api/admin/billing/pricing",
|
||||||
headers={"Authorization": f"Bearer {auth_token}"}
|
headers={"Authorization": f"Bearer {auth_token}"}
|
||||||
|
|||||||
@ -1,25 +1,12 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import pytest_asyncio
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from tortoise.contrib.test import initializer, finalizer
|
|
||||||
from mywebdav.models import User
|
from mywebdav.models import User
|
||||||
from mywebdav.billing.models import Invoice, InvoiceLineItem, PricingConfig, UsageAggregate, UserSubscription
|
from mywebdav.billing.models import Invoice, InvoiceLineItem, PricingConfig, UsageAggregate, UserSubscription
|
||||||
from mywebdav.billing.invoice_generator import InvoiceGenerator
|
from mywebdav.billing.invoice_generator import InvoiceGenerator
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest_asyncio.fixture
|
||||||
def event_loop():
|
|
||||||
import asyncio
|
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
|
||||||
yield loop
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", autouse=True)
|
|
||||||
async def initialize_tests():
|
|
||||||
initializer(["mywebdav.models", "mywebdav.billing.models"], db_url="sqlite://:memory:")
|
|
||||||
yield
|
|
||||||
await finalizer()
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
async def test_user():
|
async def test_user():
|
||||||
user = await User.create(
|
user = await User.create(
|
||||||
username="testuser",
|
username="testuser",
|
||||||
@ -30,7 +17,7 @@ async def test_user():
|
|||||||
yield user
|
yield user
|
||||||
await user.delete()
|
await user.delete()
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest_asyncio.fixture
|
||||||
async def pricing_config():
|
async def pricing_config():
|
||||||
configs = []
|
configs = []
|
||||||
configs.append(await PricingConfig.create(
|
configs.append(await PricingConfig.create(
|
||||||
@ -170,8 +157,14 @@ async def test_mark_invoice_paid(test_user, pricing_config):
|
|||||||
await paid.delete()
|
await paid.delete()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_invoice_with_tax(test_user):
|
async def test_invoice_with_tax(test_user, pricing_config):
|
||||||
await PricingConfig.filter(config_key="tax_rate_default").update(config_value=Decimal("0.21"))
|
# Update tax rate
|
||||||
|
updated = await PricingConfig.filter(config_key="tax_rate_default").update(config_value=Decimal("0.21"))
|
||||||
|
assert updated == 1 # Should update 1 row
|
||||||
|
|
||||||
|
# Verify the update worked
|
||||||
|
tax_config = await PricingConfig.get(config_key="tax_rate_default")
|
||||||
|
assert tax_config.config_value == Decimal("0.21")
|
||||||
|
|
||||||
today = date.today()
|
today = date.today()
|
||||||
|
|
||||||
|
|||||||
@ -2,26 +2,12 @@ import pytest
|
|||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from tortoise.contrib.test import initializer, finalizer
|
|
||||||
from mywebdav.models import User
|
from mywebdav.models import User
|
||||||
from mywebdav.billing.models import (
|
from mywebdav.billing.models import (
|
||||||
SubscriptionPlan, UserSubscription, UsageRecord, UsageAggregate,
|
SubscriptionPlan, UserSubscription, UsageRecord, UsageAggregate,
|
||||||
Invoice, InvoiceLineItem, PricingConfig, PaymentMethod, BillingEvent
|
Invoice, InvoiceLineItem, PricingConfig, PaymentMethod, BillingEvent
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
|
||||||
def event_loop():
|
|
||||||
import asyncio
|
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
|
||||||
yield loop
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="module", autouse=True)
|
|
||||||
async def initialize_tests():
|
|
||||||
initializer(["mywebdav.models", "mywebdav.billing.models"], db_url="sqlite://:memory:")
|
|
||||||
yield
|
|
||||||
await finalizer()
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def test_user():
|
async def test_user():
|
||||||
user = await User.create(
|
user = await User.create(
|
||||||
|
|||||||
@ -1,25 +1,12 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import pytest_asyncio
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from tortoise.contrib.test import initializer, finalizer
|
|
||||||
from mywebdav.models import User, File, Folder
|
from mywebdav.models import User, File, Folder
|
||||||
from mywebdav.billing.models import UsageRecord, UsageAggregate
|
from mywebdav.billing.models import UsageRecord, UsageAggregate
|
||||||
from mywebdav.billing.usage_tracker import UsageTracker
|
from mywebdav.billing.usage_tracker import UsageTracker
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest_asyncio.fixture
|
||||||
def event_loop():
|
|
||||||
import asyncio
|
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
|
||||||
yield loop
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", autouse=True)
|
|
||||||
async def initialize_tests():
|
|
||||||
initializer(["mywebdav.models", "mywebdav.billing.models"], db_url="sqlite://:memory:")
|
|
||||||
yield
|
|
||||||
await finalizer()
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
async def test_user():
|
async def test_user():
|
||||||
user = await User.create(
|
user = await User.create(
|
||||||
username="testuser",
|
username="testuser",
|
||||||
|
|||||||
@ -1,8 +1,28 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from tortoise import Tortoise
|
||||||
|
|
||||||
|
# Initialize Tortoise at module level
|
||||||
|
async def _init_tortoise():
|
||||||
|
await Tortoise.init(
|
||||||
|
db_url="sqlite://:memory:",
|
||||||
|
modules={
|
||||||
|
"models": ["mywebdav.models"],
|
||||||
|
"billing": ["mywebdav.billing.models"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await Tortoise.generate_schemas()
|
||||||
|
|
||||||
|
# Run initialization
|
||||||
|
asyncio.run(_init_tortoise())
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def event_loop():
|
def event_loop():
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||||
yield loop
|
yield loop
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
|
async def cleanup_db():
|
||||||
|
yield
|
||||||
|
await Tortoise.close_connections()
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import pytest_asyncio
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import async_playwright, Page, expect
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def event_loop():
|
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
|
||||||
yield loop
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="function")
|
|
||||||
async def browser():
|
|
||||||
async with async_playwright() as p:
|
|
||||||
browser = await p.chromium.launch(headless=False, slow_mo=500)
|
|
||||||
yield browser
|
|
||||||
await browser.close()
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="function")
|
|
||||||
async def context(browser):
|
|
||||||
context = await browser.new_context(
|
|
||||||
viewport={"width": 1920, "height": 1080},
|
|
||||||
user_agent="Mozilla/5.0 (X11; Linux x86_64) MyWebdav E2E Tests",
|
|
||||||
ignore_https_errors=True,
|
|
||||||
service_workers='block'
|
|
||||||
)
|
|
||||||
yield context
|
|
||||||
await context.close()
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="function")
|
|
||||||
async def page(context):
|
|
||||||
page = await context.new_page()
|
|
||||||
yield page
|
|
||||||
await page.close()
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def base_url():
|
|
||||||
return "http://localhost:9004"
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="function", autouse=True)
|
|
||||||
async def login(page: Page, base_url):
|
|
||||||
print(f"Navigating to base_url: {base_url}")
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.screenshot(path="01_initial_page.png")
|
|
||||||
|
|
||||||
# If already logged in, log out first to ensure a clean state
|
|
||||||
if await page.locator('a:has-text("Logout")').is_visible():
|
|
||||||
await page.click('a:has-text("Logout")')
|
|
||||||
await page.screenshot(path="02_after_logout.png")
|
|
||||||
|
|
||||||
# Now, proceed with login or registration
|
|
||||||
login_form = page.locator('#login-form:visible')
|
|
||||||
if await login_form.count() > 0:
|
|
||||||
await login_form.locator('input[name="username"]').fill('billingtest')
|
|
||||||
await login_form.locator('input[name="password"]').fill('password123')
|
|
||||||
await page.screenshot(path="03_before_login_click.png")
|
|
||||||
await expect(page.locator('h2:has-text("Files")')).to_be_visible(timeout=10000)
|
|
||||||
else:
|
|
||||||
# If no login form, try to register
|
|
||||||
await page.click('text=Sign Up')
|
|
||||||
register_form = page.locator('#register-form:visible')
|
|
||||||
await register_form.locator('input[name="username"]').fill('billingtest')
|
|
||||||
await register_form.locator('input[name="email"]').fill('billingtest@example.com')
|
|
||||||
await register_form.locator('input[name="password"]').fill('password123')
|
|
||||||
await page.screenshot(path="05_before_register_click.png")
|
|
||||||
await register_form.locator('button[type="submit"]').click()
|
|
||||||
await expect(page.locator('h1:has-text("My Files")')).to_be_visible(timeout=10000)
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestAdmin:
|
|
||||||
|
|
||||||
async def test_01_admin_login(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Admin should see admin nav links
|
|
||||||
await expect(page.locator('a.nav-link[data-view="admin"]')).to_be_visible()
|
|
||||||
await expect(page.locator('a.nav-link[data-view="admin-billing"]')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_02_navigate_to_admin_dashboard(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('admin-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_03_view_user_management(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show user list
|
|
||||||
await expect(page.locator('.user-list')).to_be_visible()
|
|
||||||
await expect(page.locator('text=e2etestuser')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_04_view_system_statistics(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show system stats
|
|
||||||
await expect(page.locator('.system-stats')).to_be_visible()
|
|
||||||
await expect(page.locator('.stat-item')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_05_manage_user_permissions(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click on user to manage
|
|
||||||
await page.click('.user-item:has-text("e2etestuser")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Should show user details modal
|
|
||||||
await expect(page.locator('.user-details-modal')).to_be_visible()
|
|
||||||
|
|
||||||
# Change permissions
|
|
||||||
await page.select_option('select[name="role"]', 'moderator')
|
|
||||||
await page.click('button:has-text("Save")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
async def test_06_view_storage_usage(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show storage usage
|
|
||||||
await expect(page.locator('.storage-usage')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_07_navigate_to_admin_billing(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin-billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('admin-billing')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_08_view_billing_statistics(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin-billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show billing stats
|
|
||||||
await expect(page.locator('.billing-stats')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_09_manage_pricing(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin-billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click pricing management
|
|
||||||
await page.click('button:has-text("Manage Pricing")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Should show pricing form
|
|
||||||
await expect(page.locator('.pricing-form')).to_be_visible()
|
|
||||||
|
|
||||||
# Update a price
|
|
||||||
await page.fill('input[name="storage_per_gb_month"]', '0.005')
|
|
||||||
await page.click('button:has-text("Update")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
async def test_10_generate_invoice(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="admin-billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click generate invoice
|
|
||||||
await page.click('button:has-text("Generate Invoice")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Select user
|
|
||||||
await page.select_option('select[name="user"]', 'e2etestuser')
|
|
||||||
await page.click('button:has-text("Generate")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show success message
|
|
||||||
await expect(page.locator('text=Invoice generated')).to_be_visible()
|
|
||||||
@ -1,170 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestAuthFlow:
|
|
||||||
|
|
||||||
async def test_01_user_registration(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
# Click Sign Up
|
|
||||||
await page.click('text=Sign Up')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Fill registration form
|
|
||||||
await page.fill('#register-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#register-form input[name="email"]', 'e2etestuser@example.com')
|
|
||||||
await page.fill('#register-form input[name="password"]', 'testpassword123')
|
|
||||||
|
|
||||||
# Submit registration
|
|
||||||
await page.click('#register-form button[type="submit"]')
|
|
||||||
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
await expect(page.locator('text=My Files')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_02_user_login(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
# Unregister service worker to avoid interference
|
|
||||||
await page.evaluate("""
|
|
||||||
navigator.serviceWorker.getRegistrations().then(function(registrations) {
|
|
||||||
for(let registration of registrations) {
|
|
||||||
registration.unregister();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
""")
|
|
||||||
|
|
||||||
await page.reload()
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
# Fill login form
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
|
|
||||||
# Submit login
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
await expect(page.locator('text=My Files')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_03_navigate_to_files_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Files view should be active by default
|
|
||||||
await expect(page.locator('a.nav-link.active[data-view="files"]')).to_be_visible()
|
|
||||||
await expect(page.locator('file-list')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_04_navigate_to_photos_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('a.nav-link.active[data-view="photos"]')).to_be_visible()
|
|
||||||
await expect(page.locator('photo-gallery')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_05_navigate_to_shared_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="shared"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('a.nav-link.active[data-view="shared"]')).to_be_visible()
|
|
||||||
await expect(page.locator('shared-items')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_06_navigate_to_starred_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="starred"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('a.nav-link.active[data-view="starred"]')).to_be_visible()
|
|
||||||
await expect(page.locator('starred-items')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_07_navigate_to_recent_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="recent"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('a.nav-link.active[data-view="recent"]')).to_be_visible()
|
|
||||||
await expect(page.locator('recent-files')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_08_navigate_to_deleted_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="deleted"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('a.nav-link.active[data-view="deleted"]')).to_be_visible()
|
|
||||||
await expect(page.locator('deleted-files')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_09_navigate_to_billing_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('a.nav-link.active[data-view="billing"]')).to_be_visible()
|
|
||||||
await expect(page.locator('billing-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_10_user_logout(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Click logout
|
|
||||||
await page.click('#logout-btn')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should be back to login
|
|
||||||
await expect(page.locator('#login-form')).to_be_visible()
|
|
||||||
@ -1,199 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestBillingAdminFlow:
|
|
||||||
|
|
||||||
async def test_01_admin_login(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('button[type="submit"]')
|
|
||||||
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
await expect(page.locator('text=Dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_02_navigate_to_admin_billing(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('button[type="submit"]')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.click('text=Admin')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.click('text=Billing')
|
|
||||||
await page.wait_for_url("**/admin/billing")
|
|
||||||
|
|
||||||
await expect(page.locator('h2:has-text("Billing Administration")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_03_view_revenue_statistics(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.stats-cards')).to_be_visible()
|
|
||||||
await expect(page.locator('.stat-card:has-text("Total Revenue")')).to_be_visible()
|
|
||||||
await expect(page.locator('.stat-card:has-text("Total Invoices")')).to_be_visible()
|
|
||||||
await expect(page.locator('.stat-card:has-text("Pending Invoices")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_04_verify_revenue_value_display(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
revenue_stat = page.locator('.stat-card:has-text("Total Revenue") .stat-value')
|
|
||||||
await expect(revenue_stat).to_be_visible()
|
|
||||||
|
|
||||||
revenue_text = await revenue_stat.text_content()
|
|
||||||
assert '$' in revenue_text or '0' in revenue_text
|
|
||||||
|
|
||||||
async def test_05_view_pricing_configuration_table(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-config-section')).to_be_visible()
|
|
||||||
await expect(page.locator('h3:has-text("Pricing Configuration")')).to_be_visible()
|
|
||||||
await expect(page.locator('.pricing-table')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_06_verify_pricing_config_rows(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-table tbody tr')).to_have_count(6, timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('td:has-text("Storage cost per GB per month")')).to_be_visible()
|
|
||||||
await expect(page.locator('td:has-text("Bandwidth egress cost per GB")')).to_be_visible()
|
|
||||||
await expect(page.locator('td:has-text("Free tier storage")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_07_click_edit_pricing_button(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
edit_buttons = page.locator('.btn-edit')
|
|
||||||
await expect(edit_buttons.first).to_be_visible()
|
|
||||||
|
|
||||||
page.on('dialog', lambda dialog: dialog.dismiss())
|
|
||||||
await edit_buttons.first.click()
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
async def test_08_edit_pricing_value(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
dialog_handled = False
|
|
||||||
|
|
||||||
async def handle_dialog(dialog):
|
|
||||||
nonlocal dialog_handled
|
|
||||||
dialog_handled = True
|
|
||||||
await dialog.accept(text="0.005")
|
|
||||||
|
|
||||||
page.on('dialog', handle_dialog)
|
|
||||||
|
|
||||||
edit_button = page.locator('.btn-edit').first
|
|
||||||
await edit_button.click()
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
if dialog_handled:
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
async def test_09_view_invoice_generation_section(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.invoice-generation-section')).to_be_visible()
|
|
||||||
await expect(page.locator('h3:has-text("Generate Invoices")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_10_verify_invoice_generation_form(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('#invoiceYear')).to_be_visible()
|
|
||||||
await expect(page.locator('#invoiceMonth')).to_be_visible()
|
|
||||||
await expect(page.locator('#generateInvoices')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_11_set_invoice_generation_date(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#invoiceYear', '2024')
|
|
||||||
await page.fill('#invoiceMonth', '11')
|
|
||||||
|
|
||||||
year_value = await page.input_value('#invoiceYear')
|
|
||||||
month_value = await page.input_value('#invoiceMonth')
|
|
||||||
|
|
||||||
assert year_value == '2024'
|
|
||||||
assert month_value == '11'
|
|
||||||
|
|
||||||
async def test_12_click_generate_invoices_button(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#invoiceYear', '2024')
|
|
||||||
await page.fill('#invoiceMonth', '10')
|
|
||||||
|
|
||||||
page.on('dialog', lambda dialog: dialog.dismiss())
|
|
||||||
|
|
||||||
await page.click('#generateInvoices')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
async def test_13_verify_all_stat_cards_present(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
stat_cards = page.locator('.stat-card')
|
|
||||||
await expect(stat_cards).to_have_count(3)
|
|
||||||
|
|
||||||
async def test_14_verify_pricing_table_headers(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('th:has-text("Configuration")')).to_be_visible()
|
|
||||||
await expect(page.locator('th:has-text("Current Value")')).to_be_visible()
|
|
||||||
await expect(page.locator('th:has-text("Unit")')).to_be_visible()
|
|
||||||
await expect(page.locator('th:has-text("Actions")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_15_verify_all_edit_buttons_present(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
edit_buttons = page.locator('.btn-edit')
|
|
||||||
count = await edit_buttons.count()
|
|
||||||
assert count == 6
|
|
||||||
|
|
||||||
async def test_16_scroll_through_admin_dashboard(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.evaluate("window.scrollTo(0, 0)")
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
await page.evaluate("window.scrollTo(0, document.body.scrollHeight / 2)")
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
await page.evaluate("window.scrollTo(0, 0)")
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
async def test_17_verify_responsive_layout(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.admin-billing')).to_be_visible()
|
|
||||||
|
|
||||||
bounding_box = await page.locator('.admin-billing').bounding_box()
|
|
||||||
assert bounding_box['width'] > 0
|
|
||||||
assert bounding_box['height'] > 0
|
|
||||||
|
|
||||||
async def test_18_verify_page_title(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
title = await page.title()
|
|
||||||
assert title is not None
|
|
||||||
@ -1,183 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect, Page
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestBillingAPIFlow:
|
|
||||||
|
|
||||||
async def test_01_api_get_current_usage(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/usage/current",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert 'storage_gb' in data
|
|
||||||
assert 'bandwidth_down_gb_today' in data
|
|
||||||
assert 'as_of' in data
|
|
||||||
|
|
||||||
async def test_02_api_get_monthly_usage(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/usage/monthly",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert 'storage_gb_avg' in data
|
|
||||||
assert 'bandwidth_down_gb' in data
|
|
||||||
assert 'period' in data
|
|
||||||
|
|
||||||
async def test_03_api_get_subscription(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/subscription",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert 'billing_type' in data
|
|
||||||
assert 'status' in data
|
|
||||||
assert data['billing_type'] == 'pay_as_you_go'
|
|
||||||
|
|
||||||
async def test_04_api_list_invoices(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/invoices",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert isinstance(data, list)
|
|
||||||
|
|
||||||
async def test_05_api_get_pricing(self, page: Page, base_url):
|
|
||||||
response = await page.request.get(f"{base_url}/api/billing/pricing")
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert 'storage_per_gb_month' in data
|
|
||||||
assert 'bandwidth_egress_per_gb' in data
|
|
||||||
assert 'free_tier_storage_gb' in data
|
|
||||||
|
|
||||||
async def test_06_api_get_plans(self, page: Page, base_url):
|
|
||||||
response = await page.request.get(f"{base_url}/api/billing/plans")
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert isinstance(data, list)
|
|
||||||
|
|
||||||
async def test_07_api_admin_get_pricing_config(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'adminuser', 'adminpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/admin/billing/pricing",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert isinstance(data, list)
|
|
||||||
assert len(data) >= 6
|
|
||||||
|
|
||||||
async def test_08_api_admin_get_stats(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'adminuser', 'adminpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/admin/billing/stats",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert 'total_revenue' in data
|
|
||||||
assert 'total_invoices' in data
|
|
||||||
assert 'pending_invoices' in data
|
|
||||||
|
|
||||||
async def test_09_api_user_cannot_access_admin_endpoints(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/admin/billing/pricing",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status == 403
|
|
||||||
|
|
||||||
async def test_10_api_unauthorized_access_fails(self, page: Page, base_url):
|
|
||||||
response = await page.request.get(f"{base_url}/api/billing/usage/current")
|
|
||||||
|
|
||||||
assert response.status == 401
|
|
||||||
|
|
||||||
async def test_11_api_create_payment_setup_intent(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.post(
|
|
||||||
f"{base_url}/api/billing/payment-methods/setup-intent",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok or response.status == 500
|
|
||||||
|
|
||||||
async def test_12_api_get_payment_methods(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/payment-methods",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert isinstance(data, list)
|
|
||||||
|
|
||||||
async def test_13_api_response_headers(self, page: Page, base_url):
|
|
||||||
response = await page.request.get(f"{base_url}/api/billing/pricing")
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
headers = response.headers
|
|
||||||
assert 'content-type' in headers
|
|
||||||
assert 'application/json' in headers['content-type']
|
|
||||||
|
|
||||||
async def test_14_api_invalid_endpoint_returns_404(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/nonexistent",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status == 404
|
|
||||||
|
|
||||||
async def test_15_api_request_with_params(self, page: Page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/usage/monthly?year=2024&month=11",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.ok
|
|
||||||
data = await response.json()
|
|
||||||
assert 'period' in data
|
|
||||||
assert data['period'] == '2024-11'
|
|
||||||
|
|
||||||
async def _login_and_get_token(self, page: Page, base_url: str, username: str, password: str) -> str:
|
|
||||||
response = await page.request.post(
|
|
||||||
f"{base_url}/api/auth/login",
|
|
||||||
data={"username": username, "password": password}
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.ok:
|
|
||||||
data = await response.json()
|
|
||||||
return data.get('access_token', '')
|
|
||||||
|
|
||||||
return ''
|
|
||||||
@ -1,264 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
from datetime import datetime, date
|
|
||||||
from mywebdav.billing.models import UsageAggregate, Invoice
|
|
||||||
from mywebdav.models import User
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestBillingIntegrationFlow:
|
|
||||||
|
|
||||||
async def test_01_complete_user_journey(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('input[name="username"]', 'testuser')
|
|
||||||
await page.fill('input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('button[type="submit"]')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.click('text=Files')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await page.click('text=Billing')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.billing-dashboard')).to_be_visible()
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
async def test_02_verify_usage_tracking_after_operations(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
initial_storage = await page.locator('.usage-value').first.text_content()
|
|
||||||
|
|
||||||
await page.goto(f"{base_url}/files")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
current_storage = await page.locator('.usage-value').first.text_content()
|
|
||||||
assert current_storage is not None
|
|
||||||
|
|
||||||
async def test_03_verify_cost_calculation_updates(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.estimated-cost')).to_be_visible()
|
|
||||||
|
|
||||||
cost_text = await page.locator('.estimated-cost').text_content()
|
|
||||||
assert '$' in cost_text
|
|
||||||
|
|
||||||
await page.reload()
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
new_cost_text = await page.locator('.estimated-cost').text_content()
|
|
||||||
assert '$' in new_cost_text
|
|
||||||
|
|
||||||
async def test_04_verify_subscription_consistency(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
subscription_badge = page.locator('.subscription-badge')
|
|
||||||
await expect(subscription_badge).to_be_visible()
|
|
||||||
|
|
||||||
badge_classes = await subscription_badge.get_attribute('class')
|
|
||||||
assert 'active' in badge_classes or 'inactive' in badge_classes
|
|
||||||
|
|
||||||
async def test_05_verify_pricing_consistency_across_pages(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
storage_price_user = await page.locator('.pricing-item:has-text("Storage")').last.text_content()
|
|
||||||
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
config_value = await page.locator('.pricing-table tbody tr').first.locator('.config-value').text_content()
|
|
||||||
|
|
||||||
assert config_value is not None
|
|
||||||
assert storage_price_user is not None
|
|
||||||
|
|
||||||
async def test_06_admin_changes_reflect_in_user_view(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/admin/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-table')).to_be_visible()
|
|
||||||
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-card')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_07_navigation_flow_consistency(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.click('text=Billing')
|
|
||||||
await page.wait_for_url("**/billing")
|
|
||||||
|
|
||||||
await page.click('text=Dashboard')
|
|
||||||
await page.wait_for_url("**/dashboard")
|
|
||||||
|
|
||||||
await page.click('text=Billing')
|
|
||||||
await page.wait_for_url("**/billing")
|
|
||||||
|
|
||||||
await expect(page.locator('.billing-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_08_refresh_maintains_state(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
usage_before = await page.locator('.usage-value').first.text_content()
|
|
||||||
|
|
||||||
await page.reload()
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
usage_after = await page.locator('.usage-value').first.text_content()
|
|
||||||
|
|
||||||
assert usage_before is not None
|
|
||||||
assert usage_after is not None
|
|
||||||
|
|
||||||
async def test_09_multiple_tabs_data_consistency(self, context, base_url):
|
|
||||||
page1 = await context.new_page()
|
|
||||||
page2 = await context.new_page()
|
|
||||||
|
|
||||||
await page1.goto(f"{base_url}/billing")
|
|
||||||
await page1.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page2.goto(f"{base_url}/billing")
|
|
||||||
await page2.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
usage1 = await page1.locator('.usage-value').first.text_content()
|
|
||||||
usage2 = await page2.locator('.usage-value').first.text_content()
|
|
||||||
|
|
||||||
assert usage1 is not None
|
|
||||||
assert usage2 is not None
|
|
||||||
|
|
||||||
await page1.close()
|
|
||||||
await page2.close()
|
|
||||||
|
|
||||||
async def test_10_api_and_ui_data_consistency(self, page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
api_response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/usage/current",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
api_data = await api_response.json()
|
|
||||||
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-card')).to_be_visible()
|
|
||||||
|
|
||||||
assert api_data['storage_gb'] >= 0
|
|
||||||
|
|
||||||
async def test_11_error_handling_invalid_invoice_id(self, page, base_url):
|
|
||||||
token = await self._login_and_get_token(page, base_url, 'testuser', 'testpassword123')
|
|
||||||
|
|
||||||
response = await page.request.get(
|
|
||||||
f"{base_url}/api/billing/invoices/99999",
|
|
||||||
headers={"Authorization": f"Bearer {token}"}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status == 404
|
|
||||||
|
|
||||||
async def test_12_verify_responsive_design_desktop(self, page, base_url):
|
|
||||||
await page.set_viewport_size({"width": 1920, "height": 1080})
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.billing-cards')).to_be_visible()
|
|
||||||
|
|
||||||
cards = page.locator('.billing-card')
|
|
||||||
count = await cards.count()
|
|
||||||
assert count >= 3
|
|
||||||
|
|
||||||
async def test_13_verify_responsive_design_tablet(self, page, base_url):
|
|
||||||
await page.set_viewport_size({"width": 768, "height": 1024})
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.billing-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_14_verify_responsive_design_mobile(self, page, base_url):
|
|
||||||
await page.set_viewport_size({"width": 375, "height": 667})
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.billing-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_15_performance_page_load_time(self, page, base_url):
|
|
||||||
start_time = asyncio.get_event_loop().time()
|
|
||||||
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
end_time = asyncio.get_event_loop().time()
|
|
||||||
load_time = end_time - start_time
|
|
||||||
|
|
||||||
assert load_time < 5.0
|
|
||||||
|
|
||||||
async def test_16_verify_no_console_errors(self, page, base_url):
|
|
||||||
errors = []
|
|
||||||
|
|
||||||
page.on("console", lambda msg: errors.append(msg) if msg.type == "error" else None)
|
|
||||||
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
critical_errors = [e for e in errors if 'billing' in str(e).lower()]
|
|
||||||
assert len(critical_errors) == 0
|
|
||||||
|
|
||||||
async def test_17_complete_admin_workflow(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('input[name="username"]', 'adminuser')
|
|
||||||
await page.fill('input[name="password"]', 'adminpassword123')
|
|
||||||
await page.click('button[type="submit"]')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.click('text=Admin')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.click('text=Billing')
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.admin-billing')).to_be_visible()
|
|
||||||
await expect(page.locator('.stats-cards')).to_be_visible()
|
|
||||||
await expect(page.locator('.pricing-config-section')).to_be_visible()
|
|
||||||
await expect(page.locator('.invoice-generation-section')).to_be_visible()
|
|
||||||
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
async def test_18_end_to_end_billing_lifecycle(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/billing")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await expect(page.locator('.billing-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-card')).to_be_visible()
|
|
||||||
await expect(page.locator('.cost-card')).to_be_visible()
|
|
||||||
await expect(page.locator('.pricing-card')).to_be_visible()
|
|
||||||
await expect(page.locator('.invoices-section')).to_be_visible()
|
|
||||||
await expect(page.locator('.payment-methods-section')).to_be_visible()
|
|
||||||
|
|
||||||
await page.wait_for_timeout(3000)
|
|
||||||
|
|
||||||
async def _login_and_get_token(self, page, base_url, username, password):
|
|
||||||
response = await page.request.post(
|
|
||||||
f"{base_url}/api/auth/login",
|
|
||||||
data={"username": username, "password": password}
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.ok:
|
|
||||||
data = await response.json()
|
|
||||||
return data.get('access_token', '')
|
|
||||||
|
|
||||||
return ''
|
|
||||||
@ -1,264 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestBillingUserFlow:
|
|
||||||
|
|
||||||
async def test_01_user_registration(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.click('text=Sign Up')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
await page.fill('#register-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#register-form input[name="email"]', 'billingtest@example.com')
|
|
||||||
await page.fill('#register-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#register-form button[type="submit"]')
|
|
||||||
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
await expect(page.locator('text=My Files')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_02_user_login(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.evaluate("""
|
|
||||||
navigator.serviceWorker.getRegistrations().then(function(registrations) {
|
|
||||||
for(let registration of registrations) {
|
|
||||||
registration.unregister();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
""")
|
|
||||||
|
|
||||||
await page.reload()
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
await expect(page.locator('text=My Files')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_03_navigate_to_billing_dashboard(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('billing-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_04_view_current_usage(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-card')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Current Usage')).to_be_visible()
|
|
||||||
await expect(page.locator('.usage-label:has-text("Storage")')).to_be_visible()
|
|
||||||
await expect(page.locator('.usage-label:has-text("Bandwidth")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_05_view_estimated_cost(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.cost-card')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Estimated Monthly Cost')).to_be_visible()
|
|
||||||
await expect(page.locator('.estimated-cost')).to_be_visible()
|
|
||||||
|
|
||||||
cost_text = await page.locator('.estimated-cost').text_content()
|
|
||||||
assert '$' in cost_text
|
|
||||||
|
|
||||||
async def test_06_view_pricing_information(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-card')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Current Pricing')).to_be_visible()
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-item:has-text("Storage")')).to_be_visible()
|
|
||||||
await expect(page.locator('.pricing-item:has-text("Bandwidth")')).to_be_visible()
|
|
||||||
await expect(page.locator('.pricing-item:has-text("Free Tier")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_07_view_invoice_history_empty(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.invoices-section')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Recent Invoices')).to_be_visible()
|
|
||||||
|
|
||||||
no_invoices = page.locator('.no-invoices')
|
|
||||||
if await no_invoices.is_visible():
|
|
||||||
await expect(no_invoices).to_contain_text('No invoices yet')
|
|
||||||
|
|
||||||
async def test_08_upload_file_to_track_usage(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.set_input_files('input[type="file"]', {
|
|
||||||
'name': 'test-file.txt',
|
|
||||||
'mimeType': 'text/plain',
|
|
||||||
'buffer': b'This is a test file for billing usage tracking.'
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.click('button:has-text("Upload")')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_09_verify_usage_updated_after_upload(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
storage_value = page.locator('.usage-item:has(.usage-label:has-text("Storage")) .usage-value')
|
|
||||||
await expect(storage_value).to_be_visible()
|
|
||||||
|
|
||||||
async def test_10_add_payment_method_button(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.payment-methods-section')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Payment Methods')).to_be_visible()
|
|
||||||
await expect(page.locator('#addPaymentMethod')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_11_click_add_payment_method(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
page.on('dialog', lambda dialog: dialog.accept())
|
|
||||||
|
|
||||||
await page.click('#addPaymentMethod')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
async def test_12_view_subscription_status(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.subscription-badge')).to_be_visible()
|
|
||||||
|
|
||||||
badge_text = await page.locator('.subscription-badge').text_content()
|
|
||||||
assert badge_text in ['Pay As You Go', 'Free', 'Active']
|
|
||||||
|
|
||||||
async def test_13_verify_free_tier_display(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-info:has-text("GB included free")')).to_be_visible()
|
|
||||||
|
|
||||||
free_tier_info = await page.locator('.usage-info').text_content()
|
|
||||||
assert '15' in free_tier_info or 'GB' in free_tier_info
|
|
||||||
|
|
||||||
async def test_14_verify_progress_bar(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-progress')).to_be_visible()
|
|
||||||
await expect(page.locator('.usage-progress-bar')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_15_verify_cost_breakdown(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('.cost-breakdown')).to_be_visible()
|
|
||||||
await expect(page.locator('.cost-item:has-text("Storage")')).to_be_visible()
|
|
||||||
await expect(page.locator('.cost-item:has-text("Bandwidth")')).to_be_visible()
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect, Page
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestBillingUserFlowRefactored:
|
|
||||||
|
|
||||||
async def test_navigate_to_billing_dashboard(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
await expect(page.locator('billing-dashboard')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_view_current_usage(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-card')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Current Usage')).to_be_visible()
|
|
||||||
await expect(page.locator('.usage-label:has-text("Storage")')).to_be_visible()
|
|
||||||
await expect(page.locator('.usage-label:has-text("Bandwidth")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_view_estimated_cost(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.cost-card')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Estimated Monthly Cost')).to_be_visible()
|
|
||||||
await expect(page.locator('.estimated-cost')).to_be_visible()
|
|
||||||
|
|
||||||
cost_text = await page.locator('.estimated-cost').text_content()
|
|
||||||
assert '$' in cost_text
|
|
||||||
|
|
||||||
async def test_view_pricing_information(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-card')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Current Pricing')).to_be_visible()
|
|
||||||
|
|
||||||
await expect(page.locator('.pricing-item:has-text("Storage")')).to_be_visible()
|
|
||||||
await expect(page.locator('.pricing-item:has-text("Bandwidth")')).to_be_visible()
|
|
||||||
await expect(page.locator('.pricing-item:has-text("Free Tier")')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_view_invoice_history_empty(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.invoices-section')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Recent Invoices')).to_be_visible()
|
|
||||||
|
|
||||||
no_invoices = page.locator('.no-invoices')
|
|
||||||
if await no_invoices.is_visible():
|
|
||||||
await expect(no_invoices).to_contain_text('No invoices yet')
|
|
||||||
|
|
||||||
async def test_upload_file_to_track_usage(self, page: Page):
|
|
||||||
# Navigate back to files view
|
|
||||||
await page.click('a.nav-link[data-view="files"]')
|
|
||||||
await expect(page.locator('h2:has-text("Files")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await page.set_input_files('input[type="file"]', {
|
|
||||||
'name': 'test-file.txt',
|
|
||||||
'mimeType': 'text/plain',
|
|
||||||
'buffer': b'This is a test file for billing usage tracking.'
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.click('button:has-text("Upload")')
|
|
||||||
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible(timeout=10000)
|
|
||||||
|
|
||||||
async def test_verify_usage_updated_after_upload(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
storage_value_locator = page.locator('.usage-item:has(.usage-label:has-text("Storage")) .usage-value')
|
|
||||||
await expect(storage_value_locator).not_to_contain_text("0 B", timeout=10000)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_add_payment_method_button(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.payment-methods-section')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Payment Methods')).to_be_visible()
|
|
||||||
await expect(page.locator('#addPaymentMethod')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_click_add_payment_method(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
page.on('dialog', lambda dialog: dialog.accept())
|
|
||||||
|
|
||||||
await page.click('#addPaymentMethod')
|
|
||||||
# We can't assert much here as it likely navigates to Stripe
|
|
||||||
await page.wait_for_timeout(1000) # Allow time for navigation
|
|
||||||
|
|
||||||
async def test_view_subscription_status(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.subscription-badge')).to_be_visible()
|
|
||||||
|
|
||||||
badge_text = await page.locator('.subscription-badge').text_content()
|
|
||||||
assert badge_text in ['Pay As You Go', 'Free', 'Active']
|
|
||||||
|
|
||||||
async def test_verify_free_tier_display(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-info:has-text("GB included free")')).to_be_visible()
|
|
||||||
|
|
||||||
free_tier_info = await page.locator('.usage-info').text_content()
|
|
||||||
assert '15' in free_tier_info or 'GB' in free_tier_info
|
|
||||||
|
|
||||||
async def test_verify_progress_bar(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.usage-progress')).to_be_visible()
|
|
||||||
await expect(page.locator('.usage-progress-bar')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_verify_cost_breakdown(self, page: Page):
|
|
||||||
await page.click('a.nav-link[data-view="billing"]')
|
|
||||||
await expect(page.locator('h1:has-text("Billing & Usage")')).to_be_visible(timeout=5000)
|
|
||||||
|
|
||||||
await expect(page.locator('.cost-breakdown')).to_be_visible()
|
|
||||||
await expect(page.locator('.cost-item:has-text("Storage")')).to_be_visible()
|
|
||||||
await expect(page.locator('.cost-item:has-text("Bandwidth")')).to_be_visible()
|
|
||||||
@ -1,222 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestFileManagement:
|
|
||||||
|
|
||||||
async def test_01_upload_file(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Upload a file
|
|
||||||
await page.set_input_files('input[type="file"]', {
|
|
||||||
'name': 'test-file.txt',
|
|
||||||
'mimeType': 'text/plain',
|
|
||||||
'buffer': b'This is a test file for e2e testing.'
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.click('button:has-text("Upload")')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_02_create_folder(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Click create folder button
|
|
||||||
await page.click('button:has-text("New Folder")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Fill folder name
|
|
||||||
await page.fill('input[placeholder="Folder name"]', 'Test Folder')
|
|
||||||
await page.click('button:has-text("Create")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=Test Folder')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_03_navigate_into_folder(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Click on the folder
|
|
||||||
await page.click('text=Test Folder')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should be in the folder, breadcrumb should show it
|
|
||||||
await expect(page.locator('.breadcrumb')).to_contain_text('Test Folder')
|
|
||||||
|
|
||||||
async def test_04_upload_file_in_folder(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Navigate to folder
|
|
||||||
await page.click('text=Test Folder')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Upload file in folder
|
|
||||||
await page.set_input_files('input[type="file"]', {
|
|
||||||
'name': 'folder-file.txt',
|
|
||||||
'mimeType': 'text/plain',
|
|
||||||
'buffer': b'This file is in a folder.'
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.click('button:has-text("Upload")')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=folder-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_05_navigate_back_from_folder(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Navigate to folder
|
|
||||||
await page.click('text=Test Folder')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click breadcrumb to go back
|
|
||||||
await page.click('.breadcrumb a:first-child')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should be back in root
|
|
||||||
await expect(page.locator('text=Test Folder')).to_be_visible()
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_06_select_and_delete_file(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Select file
|
|
||||||
await page.click('.file-item:has-text("test-file.txt") .file-checkbox')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click delete
|
|
||||||
await page.click('button:has-text("Delete")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Confirm delete
|
|
||||||
await page.click('button:has-text("Confirm")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# File should be gone
|
|
||||||
await expect(page.locator('text=test-file.txt')).not_to_be_visible()
|
|
||||||
|
|
||||||
async def test_07_restore_deleted_file(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Go to deleted files
|
|
||||||
await page.click('a.nav-link[data-view="deleted"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Select deleted file
|
|
||||||
await page.click('.file-item:has-text("test-file.txt") .file-checkbox')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click restore
|
|
||||||
await page.click('button:has-text("Restore")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Go back to files
|
|
||||||
await page.click('a.nav-link[data-view="files"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# File should be back
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_08_rename_folder(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Right-click on folder or use context menu
|
|
||||||
await page.click('.folder-item:has-text("Test Folder")', button='right')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click rename
|
|
||||||
await page.click('text=Rename')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Fill new name
|
|
||||||
await page.fill('input[placeholder="New name"]', 'Renamed Folder')
|
|
||||||
await page.click('button:has-text("Rename")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=Renamed Folder')).to_be_visible()
|
|
||||||
await expect(page.locator('text=Test Folder')).not_to_be_visible()
|
|
||||||
|
|
||||||
async def test_09_star_file(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Click star on file
|
|
||||||
await page.click('.file-item:has-text("test-file.txt") .star-btn')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Go to starred
|
|
||||||
await page.click('a.nav-link[data-view="starred"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_10_search_files(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Type in search
|
|
||||||
await page.fill('#search-input', 'test-file')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show search results
|
|
||||||
await expect(page.locator('.search-results')).to_be_visible()
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
@ -1,227 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestPhotoGallery:
|
|
||||||
|
|
||||||
async def test_01_upload_image_file(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Upload an image file
|
|
||||||
await page.set_input_files('input[type="file"]', {
|
|
||||||
'name': 'test-image.jpg',
|
|
||||||
'mimeType': 'image/jpeg',
|
|
||||||
'buffer': b'fake image data' # In real test, use actual image bytes
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.click('button:has-text("Upload")')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=test-image.jpg')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_02_navigate_to_photo_gallery(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('photo-gallery')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_03_view_image_in_gallery(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show uploaded image
|
|
||||||
await expect(page.locator('.photo-item')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_04_click_on_photo_to_preview(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click on photo
|
|
||||||
await page.click('.photo-item img')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should open preview
|
|
||||||
await expect(page.locator('file-preview')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_05_close_photo_preview(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click on photo
|
|
||||||
await page.click('.photo-item img')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Close preview
|
|
||||||
await page.click('.preview-close-btn')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Preview should be closed
|
|
||||||
await expect(page.locator('file-preview')).not_to_be_visible()
|
|
||||||
|
|
||||||
async def test_06_upload_multiple_images(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Upload multiple images
|
|
||||||
await page.set_input_files('input[type="file"]', [
|
|
||||||
{
|
|
||||||
'name': 'image1.jpg',
|
|
||||||
'mimeType': 'image/jpeg',
|
|
||||||
'buffer': b'fake image 1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'image2.png',
|
|
||||||
'mimeType': 'image/png',
|
|
||||||
'buffer': b'fake image 2'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
await page.click('button:has-text("Upload")')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Go to gallery
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show multiple photos
|
|
||||||
photo_count = await page.locator('.photo-item').count()
|
|
||||||
assert photo_count >= 2
|
|
||||||
|
|
||||||
async def test_07_filter_photos_by_date(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click date filter
|
|
||||||
await page.click('.date-filter-btn')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Select today
|
|
||||||
await page.click('text=Today')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should filter photos
|
|
||||||
await expect(page.locator('.photo-item')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_08_search_photos(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Type in gallery search
|
|
||||||
await page.fill('.gallery-search-input', 'image1')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show filtered results
|
|
||||||
await expect(page.locator('text=image1.jpg')).to_be_visible()
|
|
||||||
await expect(page.locator('text=image2.png')).not_to_be_visible()
|
|
||||||
|
|
||||||
async def test_09_create_album(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click create album
|
|
||||||
await page.click('button:has-text("Create Album")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Fill album name
|
|
||||||
await page.fill('input[name="album-name"]', 'Test Album')
|
|
||||||
await page.click('button:has-text("Create")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show album
|
|
||||||
await expect(page.locator('text=Test Album')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_10_add_photos_to_album(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
await page.click('a.nav-link[data-view="photos"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Select photos
|
|
||||||
await page.click('.photo-item .photo-checkbox', { force: True })
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click add to album
|
|
||||||
await page.click('button:has-text("Add to Album")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Select album
|
|
||||||
await page.click('text=Test Album')
|
|
||||||
await page.click('button:has-text("Add")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show success
|
|
||||||
await expect(page.locator('text=Photos added to album')).to_be_visible()
|
|
||||||
@ -1,207 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from playwright.async_api import expect
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestSharing:
|
|
||||||
|
|
||||||
async def test_01_share_file_with_user(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Select file
|
|
||||||
await page.click('.file-item:has-text("test-file.txt") .file-checkbox')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click share
|
|
||||||
await page.click('button:has-text("Share")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Share modal should appear
|
|
||||||
await expect(page.locator('share-modal')).to_be_visible()
|
|
||||||
|
|
||||||
# Fill share form
|
|
||||||
await page.fill('input[placeholder="Username to share with"]', 'billingtest')
|
|
||||||
await page.select_option('select[name="permission"]', 'read')
|
|
||||||
await page.click('button:has-text("Share")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Modal should close
|
|
||||||
await expect(page.locator('share-modal')).not_to_be_visible()
|
|
||||||
|
|
||||||
async def test_02_view_shared_items_as_recipient(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Go to shared items
|
|
||||||
await page.click('a.nav-link[data-view="shared"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_03_download_shared_file(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Go to shared items
|
|
||||||
await page.click('a.nav-link[data-view="shared"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click download on shared file
|
|
||||||
await page.click('.file-item:has-text("test-file.txt") .download-btn')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should trigger download (can't easily test actual download in e2e)
|
|
||||||
|
|
||||||
async def test_04_create_public_link(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Select file
|
|
||||||
await page.click('.file-item:has-text("test-file.txt") .file-checkbox')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click share
|
|
||||||
await page.click('button:has-text("Share")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click create public link
|
|
||||||
await page.click('button:has-text("Create Public Link")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should show link
|
|
||||||
await expect(page.locator('.public-link')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_05_access_public_link(self, page, base_url):
|
|
||||||
# First get the public link from previous test
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Get the public link
|
|
||||||
await page.click('.file-item:has-text("test-file.txt") .file-checkbox')
|
|
||||||
await page.click('button:has-text("Share")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
link_element = page.locator('.public-link input')
|
|
||||||
public_url = await link_element.get_attribute('value')
|
|
||||||
|
|
||||||
# Logout
|
|
||||||
await page.click('#logout-btn')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Access public link
|
|
||||||
await page.goto(public_url)
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
# Should be able to view/download the file
|
|
||||||
await expect(page.locator('text=test-file.txt')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_06_revoke_share(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Go to shared items view (as owner)
|
|
||||||
await page.click('a.nav-link[data-view="shared"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Find the share and revoke
|
|
||||||
await page.click('.share-item .revoke-btn')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Confirm revoke
|
|
||||||
await page.click('button:has-text("Confirm")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Share should be gone
|
|
||||||
await expect(page.locator('.share-item')).not_to_be_visible()
|
|
||||||
|
|
||||||
async def test_07_share_folder(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'e2etestuser')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'testpassword123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Select folder
|
|
||||||
await page.click('.folder-item:has-text("Renamed Folder") .folder-checkbox')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Click share
|
|
||||||
await page.click('button:has-text("Share")')
|
|
||||||
await page.wait_for_timeout(500)
|
|
||||||
|
|
||||||
# Share modal should appear
|
|
||||||
await expect(page.locator('share-modal')).to_be_visible()
|
|
||||||
|
|
||||||
# Fill share form
|
|
||||||
await page.fill('input[placeholder="Username to share with"]', 'billingtest')
|
|
||||||
await page.select_option('select[name="permission"]', 'read')
|
|
||||||
await page.click('button:has-text("Share")')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
async def test_08_view_shared_folder_as_recipient(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Go to shared items
|
|
||||||
await page.click('a.nav-link[data-view="shared"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
await expect(page.locator('text=Renamed Folder')).to_be_visible()
|
|
||||||
|
|
||||||
async def test_09_navigate_into_shared_folder(self, page, base_url):
|
|
||||||
await page.goto(f"{base_url}/")
|
|
||||||
await page.wait_for_load_state("networkidle")
|
|
||||||
|
|
||||||
await page.fill('#login-form input[name="username"]', 'billingtest')
|
|
||||||
await page.fill('#login-form input[name="password"]', 'password123')
|
|
||||||
await page.click('#login-form button[type="submit"]')
|
|
||||||
await page.wait_for_timeout(2000)
|
|
||||||
|
|
||||||
# Go to shared items
|
|
||||||
await page.click('a.nav-link[data-view="shared"]')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Click on shared folder
|
|
||||||
await page.click('text=Renamed Folder')
|
|
||||||
await page.wait_for_timeout(1000)
|
|
||||||
|
|
||||||
# Should see the file inside
|
|
||||||
await expect(page.locator('text=folder-file.txt')).to_be_visible()
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import os
|
|
||||||
import importlib.util
|
|
||||||
|
|
||||||
def test_e2e_conftest_exists():
|
|
||||||
conftest_path = os.path.join(os.path.dirname(__file__), 'conftest.py')
|
|
||||||
assert os.path.exists(conftest_path)
|
|
||||||
|
|
||||||
def test_e2e_test_files_exist():
|
|
||||||
test_dir = os.path.dirname(__file__)
|
|
||||||
expected_files = [
|
|
||||||
'test_billing_user_flow.py',
|
|
||||||
'test_billing_admin_flow.py',
|
|
||||||
'test_billing_api_flow.py',
|
|
||||||
'test_billing_integration_flow.py'
|
|
||||||
]
|
|
||||||
|
|
||||||
for file in expected_files:
|
|
||||||
file_path = os.path.join(test_dir, file)
|
|
||||||
assert os.path.exists(file_path), f"{file} should exist"
|
|
||||||
|
|
||||||
def test_e2e_readme_exists():
|
|
||||||
readme_path = os.path.join(os.path.dirname(__file__), 'README.md')
|
|
||||||
assert os.path.exists(readme_path)
|
|
||||||
|
|
||||||
def test_user_flow_test_class_exists():
|
|
||||||
from . import test_billing_user_flow
|
|
||||||
assert hasattr(test_billing_user_flow, 'TestBillingUserFlow')
|
|
||||||
|
|
||||||
def test_admin_flow_test_class_exists():
|
|
||||||
from . import test_billing_admin_flow
|
|
||||||
assert hasattr(test_billing_admin_flow, 'TestBillingAdminFlow')
|
|
||||||
|
|
||||||
def test_api_flow_test_class_exists():
|
|
||||||
from . import test_billing_api_flow
|
|
||||||
assert hasattr(test_billing_api_flow, 'TestBillingAPIFlow')
|
|
||||||
|
|
||||||
def test_integration_flow_test_class_exists():
|
|
||||||
from . import test_billing_integration_flow
|
|
||||||
assert hasattr(test_billing_integration_flow, 'TestBillingIntegrationFlow')
|
|
||||||
|
|
||||||
def test_user_flow_has_15_tests():
|
|
||||||
from . import test_billing_user_flow
|
|
||||||
test_class = test_billing_user_flow.TestBillingUserFlow
|
|
||||||
test_methods = [method for method in dir(test_class) if method.startswith('test_')]
|
|
||||||
assert len(test_methods) == 15, f"Expected 15 tests, found {len(test_methods)}"
|
|
||||||
|
|
||||||
def test_admin_flow_has_18_tests():
|
|
||||||
from . import test_billing_admin_flow
|
|
||||||
test_class = test_billing_admin_flow.TestBillingAdminFlow
|
|
||||||
test_methods = [method for method in dir(test_class) if method.startswith('test_')]
|
|
||||||
assert len(test_methods) == 18, f"Expected 18 tests, found {len(test_methods)}"
|
|
||||||
|
|
||||||
def test_api_flow_has_15_tests():
|
|
||||||
from . import test_billing_api_flow
|
|
||||||
test_class = test_billing_api_flow.TestBillingAPIFlow
|
|
||||||
test_methods = [method for method in dir(test_class) if method.startswith('test_')]
|
|
||||||
assert len(test_methods) == 15, f"Expected 15 tests, found {len(test_methods)}"
|
|
||||||
|
|
||||||
def test_integration_flow_has_18_tests():
|
|
||||||
from . import test_billing_integration_flow
|
|
||||||
test_class = test_billing_integration_flow.TestBillingIntegrationFlow
|
|
||||||
test_methods = [method for method in dir(test_class) if method.startswith('test_')]
|
|
||||||
assert len(test_methods) == 18, f"Expected 18 tests, found {len(test_methods)}"
|
|
||||||
|
|
||||||
def test_total_e2e_test_count():
|
|
||||||
from . import test_billing_user_flow, test_billing_admin_flow, test_billing_api_flow, test_billing_integration_flow
|
|
||||||
|
|
||||||
user_tests = len([m for m in dir(test_billing_user_flow.TestBillingUserFlow) if m.startswith('test_')])
|
|
||||||
admin_tests = len([m for m in dir(test_billing_admin_flow.TestBillingAdminFlow) if m.startswith('test_')])
|
|
||||||
api_tests = len([m for m in dir(test_billing_api_flow.TestBillingAPIFlow) if m.startswith('test_')])
|
|
||||||
integration_tests = len([m for m in dir(test_billing_integration_flow.TestBillingIntegrationFlow) if m.startswith('test_')])
|
|
||||||
|
|
||||||
total = user_tests + admin_tests + api_tests + integration_tests
|
|
||||||
assert total == 66, f"Expected 66 total tests, found {total}"
|
|
||||||
|
|
||||||
def test_conftest_has_required_fixtures():
|
|
||||||
spec = importlib.util.spec_from_file_location("conftest", os.path.join(os.path.dirname(__file__), 'conftest.py'))
|
|
||||||
conftest = importlib.util.module_from_spec(spec)
|
|
||||||
|
|
||||||
assert hasattr(conftest, 'event_loop') or True
|
|
||||||
assert hasattr(conftest, 'browser') or True
|
|
||||||
assert hasattr(conftest, 'context') or True
|
|
||||||
assert hasattr(conftest, 'page') or True
|
|
||||||
|
|
||||||
def test_playwright_installed():
|
|
||||||
try:
|
|
||||||
import playwright
|
|
||||||
assert playwright is not None
|
|
||||||
except ImportError:
|
|
||||||
pytest.fail("Playwright not installed")
|
|
||||||
|
|
||||||
def test_pytest_asyncio_installed():
|
|
||||||
try:
|
|
||||||
import pytest_asyncio
|
|
||||||
assert pytest_asyncio is not None
|
|
||||||
except ImportError:
|
|
||||||
pytest.fail("pytest-asyncio not installed")
|
|
||||||
Loading…
Reference in New Issue
Block a user