This commit is contained in:
2025-11-10 15:46:40 +01:00
parent 1ddb2c609d
commit 1e5a6dbd5f
63 changed files with 6107 additions and 491 deletions
View File
+38
View File
@@ -0,0 +1,38 @@
import pytest
import pytest_asyncio
import asyncio
from playwright.async_api import async_playwright
@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) RBox 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:8000"
+199
View File
@@ -0,0 +1,199 @@
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
+183
View File
@@ -0,0 +1,183 @@
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 ''
+264
View File
@@ -0,0 +1,264 @@
import pytest
import asyncio
from playwright.async_api import expect
from datetime import datetime, date
from rbox.billing.models import UsageAggregate, Invoice
from rbox.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 ''
+264
View File
@@ -0,0 +1,264 @@
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()
+98
View File
@@ -0,0 +1,98 @@
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")