From 55299b1eaee25ce9d73c7dce3f831a64bd09dfc7 Mon Sep 17 00:00:00 2001 From: rash Date: Wed, 18 Feb 2026 17:12:55 +0100 Subject: [PATCH] drop widget_ prefix from e2e test fixtures --- src/tests/e2e/README.md | 19 +- src/tests/e2e/conftest.py | 424 +++++++++--------- src/tests/e2e/test_css_check.py | 8 +- src/tests/e2e/test_widget_accessibility.py | 120 +++-- src/tests/e2e/test_widget_availability.py | 106 ++--- src/tests/e2e/test_widget_cart.py | 138 +++--- src/tests/e2e/test_widget_categories.py | 35 +- src/tests/e2e/test_widget_config.py | 77 ++-- src/tests/e2e/test_widget_display_modes.py | 101 ++--- src/tests/e2e/test_widget_edge_cases.py | 48 +- src/tests/e2e/test_widget_embedding.py | 68 ++- src/tests/e2e/test_widget_errors.py | 44 +- src/tests/e2e/test_widget_lightbox.py | 104 ++--- src/tests/e2e/test_widget_loading.py | 45 +- src/tests/e2e/test_widget_pricing.py | 132 +++--- .../e2e/test_widget_quantity_controls.py | 125 +++--- src/tests/e2e/test_widget_responsive.py | 33 +- src/tests/e2e/test_widget_variations.py | 79 ++-- src/tests/e2e/test_widget_vouchers.py | 46 +- src/tests/e2e/test_widget_waitinglist.py | 26 +- 20 files changed, 834 insertions(+), 944 deletions(-) diff --git a/src/tests/e2e/README.md b/src/tests/e2e/README.md index 8adf1d4502..a53c6b527c 100644 --- a/src/tests/e2e/README.md +++ b/src/tests/e2e/README.md @@ -145,11 +145,8 @@ The `widget_page` fixture provides convenient methods: ```python def test_example(widget_page, live_server_url, widget_event, widget_items): - # Navigate to event - widget_page.goto_event(live_server_url, 'testorg', 'testevent') - - # Wait for widget to load - widget_page.wait_for_widget_load() + # Navigate to event and wait for widget to load + widget_page.goto(live_server_url, 'testorg', 'testevent') # Select item quantity widget_page.select_item_quantity('General Admission', 2) @@ -177,8 +174,7 @@ def test_example(widget_page, live_server_url, widget_event, widget_items): ```python @pytest.mark.django_db def test_widget_loads(page, live_server_url, widget_organizer, widget_event, widget_items, widget_page): - widget_page.goto_event(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, widget_organizer.slug, widget_event.slug) # Verify content expect(page.locator(f'text="{widget_event.name}"')).to_be_visible() @@ -191,8 +187,7 @@ def test_widget_loads(page, live_server_url, widget_organizer, widget_event, wid def test_variations(page, live_server_url, widget_organizer, widget_event, widget_item_with_variations, widget_page): item, variations = widget_item_with_variations - widget_page.goto_event(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, widget_organizer.slug, widget_event.slug) # Expand variations widget_page.expand_variations(item.name) @@ -207,8 +202,7 @@ def test_variations(page, live_server_url, widget_organizer, widget_event, widge ```python @pytest.mark.django_db def test_checkout(page, context, live_server_url, widget_organizer, widget_event, widget_items, widget_page): - widget_page.goto_event(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, widget_organizer.slug, widget_event.slug) # Add items widget_page.select_item_quantity(widget_items[0].name, 2) @@ -447,8 +441,7 @@ class TestFeatureName: Then: expected outcome """ # Arrange - widget_page.goto_event(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, widget_organizer.slug, widget_event.slug) # Act widget_page.select_item_quantity(widget_items[0].name, 1) diff --git a/src/tests/e2e/conftest.py b/src/tests/e2e/conftest.py index c53f200fb2..dea9b18a52 100644 --- a/src/tests/e2e/conftest.py +++ b/src/tests/e2e/conftest.py @@ -84,131 +84,13 @@ def live_server_url(live_server, settings): return live_server.url - -@pytest.fixture(scope='session', autouse=True) -def _register_widget_test_view(): - """ - Register a test view that serves an HTML page with widget embedded. - - This allows E2E tests to navigate to a real URL instead of using - set_content, which causes CORS issues. - """ - from django.http import HttpResponse - from django.views import View - from django.urls import path - from pretix.multidomain import maindomain_urlconf as urls - - class WidgetTestView(View): - """Serve HTML page with widget embedded for E2E testing.""" - - # Widget attributes that can be passed as query params - WIDGET_ATTRS = [ - 'items', 'categories', 'voucher', 'disable-vouchers', - 'disable-iframe', 'subevent', 'list-type', - 'display-event-info', 'skip-ssl-check', - ] - - def get(self, request, organizer, event): - # Build URLs exactly as in working index.html - base_url = f"{request.scheme}://{request.get_host()}" - event_url = f"{base_url}/{organizer}/{event}/" - # CSS is scoped to event, JS is at root - widget_css = f"{base_url}/{organizer}/{event}/widget/v2.css" - widget_js = f"{base_url}/widget/v2.en.js" - - # Build extra attributes from query params - extra_attrs = '' - for attr in self.WIDGET_ATTRS: - val = request.GET.get(attr) - if val is not None: - if val == '': - # Boolean attribute (e.g., disable-vouchers) - extra_attrs += f' {attr}' - else: - extra_attrs += f' {attr}="{val}"' - - # Always add skip-ssl-check so iframe checkout works on HTTP - if 'skip-ssl-check' not in extra_attrs: - extra_attrs += ' skip-ssl-check' - - html = f""" - - - - - Widget Test - - - - - - -""" - resp = HttpResponse(html, content_type='text/html') - resp['Content-Security-Policy'] = "script-src * 'unsafe-inline' 'unsafe-eval'; style-src * 'unsafe-inline'" - return resp - - class ButtonTestView(View): - """Serve HTML page with pretix-button element for E2E testing.""" - - def get(self, request, organizer, event): - base_url = f"{request.scheme}://{request.get_host()}" - event_url = f"{base_url}/{organizer}/{event}/" - widget_css = f"{base_url}/{organizer}/{event}/widget/v2.css" - widget_js = f"{base_url}/widget/v2.en.js" - - # Build extra attributes from query params - extra_attrs = '' - for attr in ['items', 'voucher', 'subevent', 'disable-iframe']: - val = request.GET.get(attr) - if val is not None: - if val == '': - extra_attrs += f' {attr}' - else: - extra_attrs += f' {attr}="{val}"' - - button_text = request.GET.get('button-text', 'Buy tickets!') - - html = f""" - - - - - Button Test - - - - {button_text} - - -""" - resp = HttpResponse(html, content_type='text/html') - resp['Content-Security-Policy'] = "script-src * 'unsafe-inline' 'unsafe-eval'; style-src * 'unsafe-inline'" - return resp - - # Add URL patterns - test_pattern = path( - 'widget-test///', - WidgetTestView.as_view() - ) - button_pattern = path( - 'button-test///', - ButtonTestView.as_view() - ) - - # Insert at beginning of URL patterns - if hasattr(urls, 'urlpatterns'): - urls.urlpatterns.insert(0, test_pattern) - urls.urlpatterns.insert(0, button_pattern) - - # ============================================================================ # Test Data Fixtures - Organizers and Events # ============================================================================ @pytest.fixture @scopes_disabled() -def widget_organizer(db): +def organizer(db): """ Create an organizer for widget tests. Reuses the same pattern as existing API tests. @@ -222,15 +104,15 @@ def widget_organizer(db): @pytest.fixture @scopes_disabled() -def widget_event(widget_organizer): +def event(organizer): """Create a basic event for widget tests.""" event = Event.objects.create( - organizer=widget_organizer, + organizer=organizer, name='Test Event', slug='testevent', date_from=datetime(2026, 6, 1, 10, 0, 0, tzinfo=timezone.utc), date_to=datetime(2026, 6, 1, 18, 0, 0, tzinfo=timezone.utc), - currency='USD', + currency='EUR', live=True, testmode=False, plugins='pretix.plugins.banktransfer', @@ -243,7 +125,7 @@ def widget_event(widget_organizer): @pytest.fixture @scopes_disabled() -def widget_items(widget_event): +def items(event): """Create basic test items/products.""" from pretix.base.models import ItemCategory @@ -251,14 +133,14 @@ def widget_items(widget_event): # Create a proper category category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Tickets', position=0 ) # General Admission ticket item1 = Item.objects.create( - event=widget_event, + event=event, category=category, name='General Admission', default_price=Decimal('50.00'), @@ -269,7 +151,7 @@ def widget_items(widget_event): # VIP ticket item2 = Item.objects.create( - event=widget_event, + event=event, category=category, name='VIP Ticket', default_price=Decimal('150.00'), @@ -281,7 +163,7 @@ def widget_items(widget_event): # Create quotas for each item for item in items: quota = Quota.objects.create( - event=widget_event, + event=event, name=f'{item.name} Quota', size=100, ) @@ -296,19 +178,19 @@ def widget_items(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_with_variations(widget_event): +def item_with_variations(event): """Create an item with size variations (S, M, L, XL).""" from pretix.base.models import ItemCategory # Create category for the item category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Merchandise', position=1 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Event T-Shirt', default_price=Decimal('25.00'), @@ -334,7 +216,7 @@ def widget_item_with_variations(widget_event): # Create quota for all variations quota = Quota.objects.create( - event=widget_event, + event=event, name='T-Shirt Quota', size=50, ) @@ -348,18 +230,18 @@ def widget_item_with_variations(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_single_select(widget_event): +def item_single_select(event): """Create an item with max_per_order=1 (should show checkbox).""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='VIP', position=2 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='VIP Pass', default_price=Decimal('500.00'), @@ -369,7 +251,7 @@ def widget_item_single_select(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='VIP Quota', size=10, ) @@ -380,18 +262,18 @@ def widget_item_single_select(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_free_price(widget_event): +def item_free_price(event): """Create an item with pay-what-you-want pricing.""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Donations', position=3 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Donation', default_price=Decimal('10.00'), @@ -401,7 +283,7 @@ def widget_item_free_price(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Donation Quota', size=999, ) @@ -412,18 +294,18 @@ def widget_item_free_price(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_sold_out(widget_event): +def item_sold_out(event): """Create a sold out item.""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Early Bird', position=4 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Early Bird Ticket', default_price=Decimal('30.00'), @@ -433,7 +315,7 @@ def widget_item_sold_out(widget_event): # Create quota with size=0 (sold out) quota = Quota.objects.create( - event=widget_event, + event=event, name='Early Bird Quota', size=0, ) @@ -444,18 +326,18 @@ def widget_item_sold_out(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_free(widget_event): +def item_free(event): """Create a free item (price = 0.00).""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Free Stuff', position=10 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Free Gift', default_price=Decimal('0.00'), @@ -463,7 +345,7 @@ def widget_item_free(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Free Gift Quota', size=100, ) @@ -474,18 +356,18 @@ def widget_item_free(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_with_decimals(widget_event): +def item_with_decimals(event): """Create an item with non-zero decimal price.""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Test Category', position=11 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Half Price Item', default_price=Decimal('12.50'), @@ -493,7 +375,7 @@ def widget_item_with_decimals(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Half Price Quota', size=50, ) @@ -504,25 +386,25 @@ def widget_item_with_decimals(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_with_tax(widget_event): +def item_with_tax(event): """Create an item with tax rule.""" from pretix.base.models import ItemCategory, TaxRule # Create tax rule tax_rule = TaxRule.objects.create( - event=widget_event, + event=event, name='VAT', rate=Decimal('19.00'), # 19% VAT ) category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Taxed Items', position=12 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Taxed Product', default_price=Decimal('100.00'), @@ -531,7 +413,7 @@ def widget_item_with_tax(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Taxed Product Quota', size=50, ) @@ -546,18 +428,18 @@ def widget_item_with_tax(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_min_order(widget_event): +def item_min_order(event): """Create an item with min_per_order=2.""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Group Tickets', position=13 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Group Pass', default_price=Decimal('40.00'), @@ -566,7 +448,7 @@ def widget_item_min_order(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Group Pass Quota', size=50, ) @@ -577,18 +459,18 @@ def widget_item_min_order(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_special_chars(widget_event): +def item_special_chars(event): """Create an item with special characters in the name.""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Spezial', position=14 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Böhm & Söhne Konzert', default_price=Decimal('55.00'), @@ -596,7 +478,7 @@ def widget_item_special_chars(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Special Quota', size=50, ) @@ -611,19 +493,19 @@ def widget_item_special_chars(widget_event): @pytest.fixture @scopes_disabled() -def widget_items_with_category_description(widget_event): +def items_with_category_description(event): """Create items with a category that has a description.""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Tickets', description='Early bird tickets available', position=0 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Early Bird', default_price=Decimal('35.00'), @@ -631,7 +513,7 @@ def widget_items_with_category_description(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Early Bird Quota', size=100, ) @@ -642,24 +524,24 @@ def widget_items_with_category_description(widget_event): @pytest.fixture @scopes_disabled() -def widget_items_multiple_categories(widget_event): +def items_multiple_categories(event): """Create items in multiple categories to test grouping and ordering.""" from pretix.base.models import ItemCategory cat_music = ItemCategory.objects.create( - event=widget_event, + event=event, name='Music', position=0 ) cat_food = ItemCategory.objects.create( - event=widget_event, + event=event, name='Food & Drink', position=1 ) item1 = Item.objects.create( - event=widget_event, + event=event, category=cat_music, name='Concert Ticket', default_price=Decimal('75.00'), @@ -667,7 +549,7 @@ def widget_items_multiple_categories(widget_event): ) item2 = Item.objects.create( - event=widget_event, + event=event, category=cat_food, name='Food Pass', default_price=Decimal('25.00'), @@ -676,7 +558,7 @@ def widget_items_multiple_categories(widget_event): for item in [item1, item2]: quota = Quota.objects.create( - event=widget_event, + event=event, name=f'{item.name} Quota', size=100, ) @@ -691,33 +573,33 @@ def widget_items_multiple_categories(widget_event): @pytest.fixture @scopes_disabled() -def widget_voucher(widget_event, widget_items): +def voucher(event, items): """Create a voucher for the event.""" voucher = Voucher.objects.create( - event=widget_event, + event=event, code='TESTCODE2024', max_usages=10, price_mode='none', ) # Clear the vouchers_exist cache so the widget picks it up - widget_event.get_cache().delete('vouchers_exist') + event.get_cache().delete('vouchers_exist') return voucher @pytest.fixture @scopes_disabled() -def widget_voucher_with_item(widget_event, widget_items): +def voucher_with_item(event, items): """Create a voucher tied to a specific item.""" - item = widget_items[0] + item = items[0] voucher = Voucher.objects.create( - event=widget_event, + event=event, code='ITEMVOUCHER', max_usages=5, price_mode='percent', value=Decimal('20.00'), # 20% off item=item, ) - widget_event.get_cache().delete('vouchers_exist') + event.get_cache().delete('vouchers_exist') return voucher @@ -727,21 +609,21 @@ def widget_voucher_with_item(widget_event, widget_items): @pytest.fixture @scopes_disabled() -def widget_item_sold_out_with_waitinglist(widget_event): +def item_sold_out_with_waitinglist(event): """Create a sold out item with waiting list enabled.""" from pretix.base.models import ItemCategory # Enable waiting list on the event - widget_event.settings.set('waiting_list_enabled', True) + event.settings.set('waiting_list_enabled', True) category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Sold Out', position=20 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Sold Out Concert', default_price=Decimal('80.00'), @@ -751,7 +633,7 @@ def widget_item_sold_out_with_waitinglist(widget_event): # Create quota with size=0 (sold out) quota = Quota.objects.create( - event=widget_event, + event=event, name='Sold Out Quota', size=0, ) @@ -766,17 +648,17 @@ def widget_item_sold_out_with_waitinglist(widget_event): @pytest.fixture @scopes_disabled() -def widget_event_series(widget_organizer): +def event_series(organizer): """Create an event series with multiple subevents, items, and quotas.""" from pretix.base.models import ItemCategory event = Event.objects.create( - organizer=widget_organizer, + organizer=organizer, name='Concert Series', slug='concert-series', date_from=datetime(2026, 6, 1, 19, 0, 0, tzinfo=timezone.utc), has_subevents=True, - currency='USD', + currency='EUR', live=True, plugins='pretix.plugins.banktransfer', ) @@ -839,15 +721,16 @@ def widget_page(page): def __init__(self, page: Page): self.page = page - def goto_widget_test_page( + def goto( self, live_server_url: str, org_slug: str, event_slug: str, + wait=True, **widget_attrs ): """ - Navigate to a test page with widget embedded. + Navigate to a test page with widget embedded and wait for it to load. Uses a Django view that serves an HTML page with the pretix widget embedded, simulating how it would be used on a customer's @@ -856,6 +739,9 @@ def widget_page(page): Extra keyword arguments are passed as query params to the view, which converts them to widget attributes. For boolean attributes (like disable-vouchers), pass an empty string as value. + + Set wait=False to skip waiting for the widget to load (useful for + tests that need to observe loading/error states). """ # Navigate to the test view URL test_url = f"{live_server_url}/widget-test/{org_slug}/{event_slug}/" @@ -863,11 +749,8 @@ def widget_page(page): from urllib.parse import urlencode test_url += '?' + urlencode(widget_attrs) self.page.goto(test_url) - return self - - def goto_event(self, live_server_url: str, org_slug: str, event_slug: str): - """Navigate to an event page.""" - self.page.goto(f"{live_server_url}/{org_slug}/{event_slug}/") + if wait: + self.wait_for_widget_load() return self def wait_for_widget_load(self): @@ -975,18 +858,18 @@ def widget_page(page): @pytest.fixture @scopes_disabled() -def widget_item_require_voucher(widget_event): +def item_require_voucher(event): """Create an item that requires a voucher to purchase.""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Voucher Only', position=30 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Exclusive Pass', default_price=Decimal('200.00'), @@ -995,7 +878,7 @@ def widget_item_require_voucher(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Exclusive Quota', size=50, ) @@ -1006,18 +889,18 @@ def widget_item_require_voucher(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_low_stock(widget_event): +def item_low_stock(event): """Create an item with low stock (quota_left visible).""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Limited', position=31 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Last Chance Ticket', default_price=Decimal('65.00'), @@ -1025,32 +908,32 @@ def widget_item_low_stock(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Limited Quota', size=3, ) quota.items.add(item) # Enable "show quota left" on the event - widget_event.settings.set('show_quota_left', True) + event.settings.set('show_quota_left', True) return item @pytest.fixture @scopes_disabled() -def widget_item_not_yet_available(widget_event): +def item_not_yet_available(event): """Create an item that is not yet available (future available_from).""" from pretix.base.models import ItemCategory category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Coming Soon', position=32 ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Future Ticket', default_price=Decimal('45.00'), @@ -1060,7 +943,7 @@ def widget_item_not_yet_available(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Future Quota', size=100, ) @@ -1075,7 +958,7 @@ def widget_item_not_yet_available(widget_event): @pytest.fixture @scopes_disabled() -def widget_item_with_picture(widget_event): +def item_with_picture(event): """Create an item with a product picture.""" from pretix.base.models import ItemCategory from django.core.files.uploadedfile import SimpleUploadedFile @@ -1083,7 +966,7 @@ def widget_item_with_picture(widget_event): from PIL import Image as PILImage category = ItemCategory.objects.create( - event=widget_event, + event=event, name='Gallery Items', position=40 ) @@ -1101,7 +984,7 @@ def widget_item_with_picture(widget_event): ) item = Item.objects.create( - event=widget_event, + event=event, category=category, name='Art Print', default_price=Decimal('35.00'), @@ -1111,7 +994,7 @@ def widget_item_with_picture(widget_event): ) quota = Quota.objects.create( - event=widget_event, + event=event, name='Art Print Quota', size=50, ) @@ -1144,3 +1027,118 @@ def cross_browser_page(request, playwright): page.close() context.close() browser.close() + + +@pytest.fixture(scope='session', autouse=True) +def _register_widget_test_view(): + """ + Register a test view that serves an HTML page with widget embedded. + + This allows E2E tests to navigate to a real URL instead of using + set_content, which causes CORS issues. + """ + from django.http import HttpResponse + from django.views import View + from django.urls import path + from pretix.multidomain import maindomain_urlconf as urls + + class WidgetTestView(View): + """Serve HTML page with widget embedded for E2E testing.""" + + # Widget attributes that can be passed as query params + WIDGET_ATTRS = [ + 'items', 'categories', 'voucher', 'disable-vouchers', + 'disable-iframe', 'subevent', 'list-type', + 'display-event-info', 'skip-ssl-check', + ] + + def get(self, request, organizer, event): + base_url = f"{request.scheme}://{request.get_host()}" + event_url = f"{base_url}/{organizer}/{event}/" + widget_css = f"{base_url}/{organizer}/{event}/widget/v2.css" + widget_js = f"{base_url}/widget/v2.en.js" + + # Build extra attributes from query params + extra_attrs = '' + for attr in self.WIDGET_ATTRS: + val = request.GET.get(attr) + if val is not None: + if val == '': + # Boolean attribute (e.g., disable-vouchers) + extra_attrs += f' {attr}' + else: + extra_attrs += f' {attr}="{val}"' + + # Always add skip-ssl-check so iframe checkout works on HTTP + if 'skip-ssl-check' not in extra_attrs: + extra_attrs += ' skip-ssl-check' + + html = f""" + + + + + Widget Test + + + + + + +""" + resp = HttpResponse(html, content_type='text/html') + resp['Content-Security-Policy'] = "script-src * 'unsafe-inline' 'unsafe-eval'; style-src * 'unsafe-inline'" + return resp + + class ButtonTestView(View): + """Serve HTML page with pretix-button element for E2E testing.""" + + def get(self, request, organizer, event): + base_url = f"{request.scheme}://{request.get_host()}" + event_url = f"{base_url}/{organizer}/{event}/" + widget_css = f"{base_url}/{organizer}/{event}/widget/v2.css" + widget_js = f"{base_url}/widget/v2.en.js" + + # Build extra attributes from query params + extra_attrs = '' + for attr in ['items', 'voucher', 'subevent', 'disable-iframe']: + val = request.GET.get(attr) + if val is not None: + if val == '': + extra_attrs += f' {attr}' + else: + extra_attrs += f' {attr}="{val}"' + + button_text = request.GET.get('button-text', 'Buy tickets!') + + html = f""" + + + + + Button Test + + + + {button_text} + + +""" + resp = HttpResponse(html, content_type='text/html') + resp['Content-Security-Policy'] = "script-src * 'unsafe-inline' 'unsafe-eval'; style-src * 'unsafe-inline'" + return resp + + # Add URL patterns + test_pattern = path( + 'widget-test///', + WidgetTestView.as_view() + ) + button_pattern = path( + 'button-test///', + ButtonTestView.as_view() + ) + + # Insert at beginning of URL patterns + if hasattr(urls, 'urlpatterns'): + urls.urlpatterns.insert(0, test_pattern) + urls.urlpatterns.insert(0, button_pattern) diff --git a/src/tests/e2e/test_css_check.py b/src/tests/e2e/test_css_check.py index 706add970d..82c3a51dbb 100644 --- a/src/tests/e2e/test_css_check.py +++ b/src/tests/e2e/test_css_check.py @@ -9,13 +9,13 @@ from playwright.sync_api import Page def test_css_contains_no_scss_syntax( page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items + organizer, + event, + items ): """Verify CSS is compiled and doesn't contain SCSS syntax.""" # Fetch the CSS directly - css_url = f"{live_server_url}/{widget_organizer.slug}/{widget_event.slug}/widget/v2.css" + css_url = f"{live_server_url}/{organizer.slug}/{event.slug}/widget/v2.css" print(f"\nFetching CSS from: {css_url}") response = page.request.get(css_url) diff --git a/src/tests/e2e/test_widget_accessibility.py b/src/tests/e2e/test_widget_accessibility.py index efbaa487d9..e44adae165 100644 --- a/src/tests/e2e/test_widget_accessibility.py +++ b/src/tests/e2e/test_widget_accessibility.py @@ -23,38 +23,36 @@ class TestWidgetAriaLabels: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Main widget wrapper should have role="article" and aria-label with event name. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) wrapper = page.locator('.pretix-widget-wrapper') expect(wrapper).to_have_attribute('role', 'article') - expect(wrapper).to_have_attribute('aria-label', widget_event.name) + expect(wrapper).to_have_attribute('aria-label', event.name) def test_widget_wrapper_is_focusable( self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Widget wrapper should have tabindex="0" for keyboard access. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) wrapper = page.locator('.pretix-widget-wrapper') expect(wrapper).to_have_attribute('tabindex', '0') @@ -63,9 +61,9 @@ class TestWidgetAriaLabels: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -76,13 +74,12 @@ class TestWidgetAriaLabels: is explicitly enabled for single events (auto mode hides it). We use the display-event-info attribute to force it on. """ - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, - widget_event.slug, + organizer.slug, + event.slug, **{'display-event-info': 'true'} ) - widget_page.wait_for_widget_load() heading = page.locator( '.pretix-widget-event-header strong[role="heading"]') @@ -98,21 +95,20 @@ class TestQuantityControlAccessibility: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Plus/minus buttons should have descriptive aria-labels. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Find increment button for first item item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_items[0].name}")') + f'.pretix-widget-item:has-text("{items[0].name}")') inc_btn = item_elem.locator('button[aria-label]').last dec_btn = item_elem.locator('button[aria-label]').first @@ -126,20 +122,19 @@ class TestQuantityControlAccessibility: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Quantity input should be connected to a label via aria-labelledby. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_items[0].name}")') + f'.pretix-widget-item:has-text("{items[0].name}")') qty_input = item_elem.locator('input[type="number"]') labelledby = qty_input.get_attribute('aria-labelledby') @@ -154,17 +149,16 @@ class TestVoucherAccessibility: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_voucher, + organizer, + event, + voucher, widget_page ): """ Voucher input should reference the headline via aria-labelledby. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) voucher_input = page.locator('.pretix-widget-voucher-input') headline = page.locator('.pretix-widget-voucher-headline') @@ -186,20 +180,19 @@ class TestVariationAccessibility: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ Variations toggle button should have aria-expanded and aria-controls attributes. """ - item, _ = widget_item_with_variations + item, _ = item_with_variations - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")') @@ -226,22 +219,21 @@ class TestCalendarAccessibility: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ Calendar table should have tabindex="0" and aria-labelledby. """ - event, _ = widget_event_series + event, _ = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'calendar'} ) - widget_page.wait_for_widget_load() table = page.locator('.pretix-widget-event-calendar-table') expect(table).to_have_attribute('tabindex', '0') @@ -254,23 +246,22 @@ class TestCalendarAccessibility: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ Calendar day-of-week headers should have full day names as aria-labels (Mo -> Monday, Tu -> Tuesday, etc.). """ - event, _ = widget_event_series + event, _ = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'calendar'} ) - widget_page.wait_for_widget_load() # Check first day header has aria-label first_header = page.locator( @@ -289,18 +280,17 @@ class TestKeyboardNavigation: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Pressing Tab should cycle through interactive elements (inputs, buttons) within the widget. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Tab through several elements focused_tags = set() diff --git a/src/tests/e2e/test_widget_availability.py b/src/tests/e2e/test_widget_availability.py index ca97d285c2..72eaab0c5c 100644 --- a/src/tests/e2e/test_widget_availability.py +++ b/src/tests/e2e/test_widget_availability.py @@ -20,18 +20,17 @@ class TestSoldOutState: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_sold_out, + organizer, + event, + item_sold_out, widget_page ): """Sold out item should show 'Sold out' text.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_sold_out.name}")') + f'.pretix-widget-item:has-text("{item_sold_out.name}")') expect(item_elem).to_be_visible() # Should show "Sold out" in the availability area @@ -42,18 +41,17 @@ class TestSoldOutState: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_sold_out, + organizer, + event, + item_sold_out, widget_page ): """Sold out item should not show any input controls.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_sold_out.name}")') + f'.pretix-widget-item:has-text("{item_sold_out.name}")') expect(item_elem).to_be_visible() # Should not have any input controls @@ -68,21 +66,20 @@ class TestQuotaLeftDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_low_stock, + organizer, + event, + item_low_stock, widget_page ): """ Item with low stock and show_quota_left should display the number of remaining tickets. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_low_stock.name}")') + f'.pretix-widget-item:has-text("{item_low_stock.name}")') expect(item_elem).to_be_visible() # Should show quota left text containing "3" @@ -93,18 +90,17 @@ class TestQuotaLeftDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_low_stock, + organizer, + event, + item_low_stock, widget_page ): """Low stock items should still be purchasable.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_low_stock.name}")') + f'.pretix-widget-item:has-text("{item_low_stock.name}")') expect(item_elem).to_be_visible() # Should have quantity input @@ -119,18 +115,17 @@ class TestRequireVoucherState: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_require_voucher, + organizer, + event, + item_require_voucher, widget_page ): """Item requiring voucher should show 'Only available with a voucher'.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_require_voucher.name}")') + f'.pretix-widget-item:has-text("{item_require_voucher.name}")') expect(item_elem).to_be_visible() # Should show unavailability message with voucher link @@ -142,19 +137,18 @@ class TestRequireVoucherState: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_require_voucher, - widget_voucher, + organizer, + event, + item_require_voucher, + voucher, widget_page ): """Voucher-required message should link to the voucher input field.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_require_voucher.name}")') + f'.pretix-widget-item:has-text("{item_require_voucher.name}")') unavail = item_elem.locator('.pretix-widget-availability-unavailable') # Should have a link (to jump to voucher input) @@ -170,18 +164,17 @@ class TestNotYetAvailable: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_not_yet_available, + organizer, + event, + item_not_yet_available, widget_page ): """Item with future available_from should show 'Not yet available'.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_not_yet_available.name}")') + f'.pretix-widget-item:has-text("{item_not_yet_available.name}")') expect(item_elem).to_be_visible() # Should show unavailability message @@ -192,18 +185,17 @@ class TestNotYetAvailable: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_not_yet_available, + organizer, + event, + item_not_yet_available, widget_page ): """Not-yet-available items should not have quantity controls.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_not_yet_available.name}")') + f'.pretix-widget-item:has-text("{item_not_yet_available.name}")') expect(item_elem).to_be_visible() # Should not have any input controls diff --git a/src/tests/e2e/test_widget_cart.py b/src/tests/e2e/test_widget_cart.py index 145ed3afc5..26d91fa780 100644 --- a/src/tests/e2e/test_widget_cart.py +++ b/src/tests/e2e/test_widget_cart.py @@ -21,17 +21,16 @@ class TestCartBasics: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """Selecting items and clicking Buy should open iframe checkout.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - widget_page.select_item_quantity(widget_items[0].name, 2) + widget_page.select_item_quantity(items[0].name, 2) widget_page.click_buy_button() # Iframe checkout should open @@ -48,15 +47,14 @@ class TestCartBasics: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """Clicking Buy without selecting items should not open checkout.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Don't select any items, just click buy widget_page.click_buy_button() @@ -70,7 +68,7 @@ class TestCartBasics: # Items should still be there expect(page.locator( - f'.pretix-widget-item:has-text("{widget_items[0].name}")' + f'.pretix-widget-item:has-text("{items[0].name}")' )).to_be_visible() @@ -83,17 +81,16 @@ class TestCartPersistence: page: Page, context: BrowserContext, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """Adding items to cart should create a pretix_widget cookie.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - widget_page.select_item_quantity(widget_items[0].name, 1) + widget_page.select_item_quantity(items[0].name, 1) widget_page.click_buy_button() # Wait for iframe checkout to open (cookie is set during cart creation) @@ -111,18 +108,17 @@ class TestCartPersistence: page: Page, context: BrowserContext, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """After creating a cart and reloading, widget should show resume option.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Create a real cart by adding items and opening checkout - widget_page.select_item_quantity(widget_items[0].name, 1) + widget_page.select_item_quantity(items[0].name, 1) widget_page.click_buy_button() widget_page.wait_for_iframe_checkout() @@ -150,17 +146,16 @@ class TestIframeCheckout: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """With skip-ssl-check, checkout should open in iframe on HTTP.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - widget_page.select_item_quantity(widget_items[0].name, 1) + widget_page.select_item_quantity(items[0].name, 1) widget_page.click_buy_button() widget_page.wait_for_iframe_checkout() @@ -168,23 +163,22 @@ class TestIframeCheckout: iframe_elem = page.locator('iframe[name^="pretix-widget-"]') src = iframe_elem.get_attribute('src') assert 'iframe=1' in src - assert widget_organizer.slug in src + assert organizer.slug in src def test_close_iframe_button( self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """User should be able to close checkout iframe.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - widget_page.select_item_quantity(widget_items[0].name, 1) + widget_page.select_item_quantity(items[0].name, 1) widget_page.click_buy_button() widget_page.wait_for_iframe_checkout() @@ -198,17 +192,16 @@ class TestIframeCheckout: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """Iframe checkout URL should include take_cart_id parameter.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - widget_page.select_item_quantity(widget_items[0].name, 2) + widget_page.select_item_quantity(items[0].name, 2) widget_page.click_buy_button() widget_page.wait_for_iframe_checkout() @@ -221,17 +214,16 @@ class TestIframeCheckout: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """Iframe checkout overlay container should be visible during checkout.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - widget_page.select_item_quantity(widget_items[0].name, 1) + widget_page.select_item_quantity(items[0].name, 1) widget_page.click_buy_button() widget_page.wait_for_iframe_checkout() @@ -248,19 +240,18 @@ class TestMultipleItemSelection: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """Selecting multiple items should open checkout with all of them.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Select multiple items - widget_page.select_item_quantity(widget_items[0].name, 2) - widget_page.select_item_quantity(widget_items[1].name, 1) + widget_page.select_item_quantity(items[0].name, 2) + widget_page.select_item_quantity(items[1].name, 1) widget_page.click_buy_button() @@ -275,22 +266,21 @@ class TestMultipleItemSelection: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, - widget_item_single_select, + organizer, + event, + items, + item_single_select, widget_page ): """Selecting both checkbox and quantity items should open checkout.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Select quantity item - widget_page.select_item_quantity(widget_items[0].name, 3) + widget_page.select_item_quantity(items[0].name, 3) # Select checkbox item - widget_page.select_item_quantity(widget_item_single_select.name, 1) + widget_page.select_item_quantity(item_single_select.name, 1) widget_page.click_buy_button() diff --git a/src/tests/e2e/test_widget_categories.py b/src/tests/e2e/test_widget_categories.py index 11593dd7b1..bd5fa47879 100644 --- a/src/tests/e2e/test_widget_categories.py +++ b/src/tests/e2e/test_widget_categories.py @@ -19,19 +19,18 @@ class TestCategoryDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Category names should be shown as h3 headers. - The widget_items fixture creates a 'Tickets' category. + The items fixture creates a 'Tickets' category. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Category header should be visible category_header = page.locator( @@ -42,17 +41,16 @@ class TestCategoryDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items_with_category_description, + organizer, + event, + items_with_category_description, widget_page ): """ Category descriptions should be displayed below category name. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Category description should be visible desc = page.locator('.pretix-widget-category-description') @@ -63,18 +61,17 @@ class TestCategoryDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items_multiple_categories, + organizer, + event, + items_multiple_categories, widget_page ): """ Items should be grouped under respective categories and maintain category sort order. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Both categories should be visible expect(page.locator( diff --git a/src/tests/e2e/test_widget_config.py b/src/tests/e2e/test_widget_config.py index 2b2b18a64e..5b1732d26c 100644 --- a/src/tests/e2e/test_widget_config.py +++ b/src/tests/e2e/test_widget_config.py @@ -19,25 +19,24 @@ class TestItemsFilter: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ When items="" is set, only that item should be shown. """ # Get the first item's ID - target_item = widget_items[0] - other_item = widget_items[1] + target_item = items[0] + other_item = items[1] - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, - widget_event.slug, + organizer.slug, + event.slug, items=str(target_item.pk) ) - widget_page.wait_for_widget_load() # Target item should be visible expect(page.locator( @@ -53,26 +52,25 @@ class TestItemsFilter: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ When items=",", both items should be shown. """ - ids = ','.join(str(item.pk) for item in widget_items) + ids = ','.join(str(item.pk) for item in items) - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, - widget_event.slug, + organizer.slug, + event.slug, items=ids ) - widget_page.wait_for_widget_load() # Both items should be visible - for item in widget_items: + for item in items: expect(page.locator( f'.pretix-widget-item:has-text("{item.name}")' )).to_be_visible() @@ -86,26 +84,25 @@ class TestCategoriesFilter: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items_multiple_categories, + organizer, + event, + items_multiple_categories, widget_page ): """ When categories="" is set, only items from that category should be shown. """ - items = widget_items_multiple_categories + items = items_multiple_categories # Get the category of the first item (Music) target_category = items[0].category - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, - widget_event.slug, + organizer.slug, + event.slug, categories=str(target_category.pk) ) - widget_page.wait_for_widget_load() # Music item should be visible expect(page.locator( @@ -126,9 +123,9 @@ class TestDisableVouchers: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_voucher, + organizer, + event, + voucher, widget_page ): """ @@ -136,13 +133,12 @@ class TestDisableVouchers: even when vouchers exist. """ # Navigate with disable-vouchers attribute - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, - widget_event.slug, + organizer.slug, + event.slug, **{'disable-vouchers': ''} ) - widget_page.wait_for_widget_load() # Voucher section should NOT be visible voucher_section = page.locator('.pretix-widget-voucher') @@ -157,22 +153,21 @@ class TestDisableIframe: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ When disable-iframe is set, checkout should open in a new tab instead of an iframe overlay. """ - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, - widget_event.slug, + organizer.slug, + event.slug, **{'disable-iframe': ''} ) - widget_page.wait_for_widget_load() # Select quantity for an item widget_page.select_item_quantity('General Admission', 1) @@ -183,5 +178,5 @@ class TestDisableIframe: popup = popup_info.value # New tab should navigate to checkout URL - assert widget_organizer.slug in popup.url + assert organizer.slug in popup.url popup.close() diff --git a/src/tests/e2e/test_widget_display_modes.py b/src/tests/e2e/test_widget_display_modes.py index 0b48505689..09e4f1f095 100644 --- a/src/tests/e2e/test_widget_display_modes.py +++ b/src/tests/e2e/test_widget_display_modes.py @@ -21,21 +21,20 @@ class TestWidgetMode: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Default widget mode should show full product listing with categories, items, and a buy button. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Should show items - for item in widget_items: + for item in items: expect(page.locator( f'.pretix-widget-item:has-text("{item.name}")' )).to_be_visible() @@ -54,22 +53,21 @@ class TestCalendarView: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ Calendar view should show a monthly grid with event dates. """ - event, subevents = widget_event_series + event, subevents = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'calendar'} ) - widget_page.wait_for_widget_load() # Should show calendar table expect(page.locator( @@ -84,22 +82,21 @@ class TestCalendarView: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ Clicking next month button should navigate to the next month. """ - event, subevents = widget_event_series + event, subevents = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'calendar'} ) - widget_page.wait_for_widget_load() # Get current month heading text header = page.locator('.pretix-widget-event-calendar-head') @@ -121,8 +118,8 @@ class TestCalendarView: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ @@ -133,15 +130,14 @@ class TestCalendarView: (target_url resolves to configured domain, not live_server). We verify the links exist and have correct structure. """ - event, _ = widget_event_series + event, _ = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'calendar'} ) - widget_page.wait_for_widget_load() # Event links should exist and show event info event_link = page.locator( @@ -172,22 +168,21 @@ class TestListView: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ List view should display events as a linear list. """ - event, subevents = widget_event_series + event, subevents = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'list'} ) - widget_page.wait_for_widget_load() # Should show event list entries entries = page.locator('.pretix-widget-event-list-entry') @@ -200,22 +195,21 @@ class TestListView: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ List view should show subevent names. """ - event, subevents = widget_event_series + event, subevents = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'list'} ) - widget_page.wait_for_widget_load() # At least the first subevent name should appear expect(page.locator( @@ -226,8 +220,8 @@ class TestListView: self, page: Page, live_server_url: str, - widget_organizer, - widget_event_series, + organizer, + event_series, widget_page ): """ @@ -237,15 +231,14 @@ class TestListView: (target_url resolves to configured domain, not live_server). We verify the entries have correct structure. """ - event, _ = widget_event_series + event, _ = event_series - widget_page.goto_widget_test_page( + widget_page.goto( live_server_url, - widget_organizer.slug, + organizer.slug, event.slug, **{'list-type': 'list'} ) - widget_page.wait_for_widget_load() # Each entry should show availability info first_entry = page.locator( @@ -266,9 +259,9 @@ class TestButtonMode: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -276,8 +269,8 @@ class TestButtonMode: """ widget_page.goto_button_test_page( live_server_url, - widget_organizer.slug, - widget_event.slug + organizer.slug, + event.slug ) # Wait for script to load and initialize @@ -292,9 +285,9 @@ class TestButtonMode: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -302,8 +295,8 @@ class TestButtonMode: """ widget_page.goto_button_test_page( live_server_url, - widget_organizer.slug, - widget_event.slug + organizer.slug, + event.slug ) page.wait_for_timeout(2000) @@ -313,5 +306,5 @@ class TestButtonMode: page.locator('.pretix-button').click() popup = popup_info.value - assert widget_organizer.slug in popup.url + assert organizer.slug in popup.url popup.close() diff --git a/src/tests/e2e/test_widget_edge_cases.py b/src/tests/e2e/test_widget_edge_cases.py index 74a35d3573..b99dc9bbf9 100644 --- a/src/tests/e2e/test_widget_edge_cases.py +++ b/src/tests/e2e/test_widget_edge_cases.py @@ -19,8 +19,8 @@ class TestEmptyStates: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, + organizer, + event, widget_page ): """ @@ -28,9 +28,8 @@ class TestEmptyStates: Should show the widget container but no item rows. """ # Navigate without creating any items - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Widget should still be present expect(page.locator('.pretix-widget')).to_be_visible() @@ -43,18 +42,17 @@ class TestEmptyStates: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Submitting with zero quantity should not navigate away. The widget should remain visible without opening checkout. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Don't select any items, just click buy widget_page.click_buy_button() @@ -65,7 +63,7 @@ class TestEmptyStates: # Items should still be visible expect(page.locator( - f'.pretix-widget-item:has-text("{widget_items[0].name}")' + f'.pretix-widget-item:has-text("{items[0].name}")' )).to_be_visible() @@ -77,20 +75,19 @@ class TestMinPerOrder: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_min_order, + organizer, + event, + item_min_order, widget_page ): """ Items with min_per_order should display a text message indicating the minimum quantity (e.g. "minimum amount to order: 2"). """ - item = widget_item_min_order + item = item_min_order - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")') @@ -110,20 +107,19 @@ class TestSpecialCharacters: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_special_chars, + organizer, + event, + item_special_chars, widget_page ): """ Items with special characters (umlauts, ampersands, etc.) should display correctly. """ - item = widget_item_special_chars + item = item_special_chars - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Item with special characters should be visible item_elem = page.locator( diff --git a/src/tests/e2e/test_widget_embedding.py b/src/tests/e2e/test_widget_embedding.py index edd015c4a2..5e61d9317f 100644 --- a/src/tests/e2e/test_widget_embedding.py +++ b/src/tests/e2e/test_widget_embedding.py @@ -16,9 +16,9 @@ class TestWidgetEmbedding: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -29,25 +29,24 @@ class TestWidgetEmbedding: - All configured items """ # Navigate to test page with widget embedded - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug + widget_page.goto( + live_server_url, organizer.slug, event.slug ) - widget_page.wait_for_widget_load() # Verify widget container exists with event aria-label widget = page.locator('.pretix-widget-wrapper') - expect(widget).to_have_attribute('aria-label', widget_event.name) + expect(widget).to_have_attribute('aria-label', event.name) # Verify items are listed - for item in widget_items: + for item in items: expect(page.locator(f'text="{item.name}"')).to_be_visible() def test_widget_displays_loading_state( self, page: Page, live_server_url: str, - widget_organizer, - widget_event, + organizer, + event, widget_page ): """ @@ -56,8 +55,8 @@ class TestWidgetEmbedding: The loading spinner should eventually disappear when data is loaded. """ # Navigate to widget test page - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug + widget_page.goto( + live_server_url, organizer.slug, event.slug, wait=False ) # Wait for widget element @@ -74,8 +73,8 @@ class TestWidgetEmbedding: widget_page ): """Widget should display error message for invalid event.""" - widget_page.goto_widget_test_page( - live_server_url, 'invalid-org', 'invalid-event' + widget_page.goto( + live_server_url, 'invalid-org', 'invalid-event', wait=False ) # Should show widget container @@ -90,9 +89,9 @@ class TestWidgetEmbedding: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -100,13 +99,12 @@ class TestWidgetEmbedding: Item descriptions should be visible for items that have them. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug + widget_page.goto( + live_server_url, organizer.slug, event.slug ) - widget_page.wait_for_widget_load() # Check that descriptions are shown - for item in widget_items: + for item in items: if item.description: expect( page.locator(f'text="{item.description}"') @@ -116,9 +114,9 @@ class TestWidgetEmbedding: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -126,21 +124,20 @@ class TestWidgetEmbedding: Prices should be formatted with currency and decimal places. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug + widget_page.goto( + live_server_url, organizer.slug, event.slug ) - widget_page.wait_for_widget_load() # Verify prices are shown (with currency) # Each item should have its price displayed - for item in widget_items: + for item in items: # Find the item container first, then check price within it item_container = page.locator( f'.pretix-widget-item:has-text("{item.name}")' ) expect(item_container).to_be_visible() - # Check price is present (formatted as "USD XX.XX") + # Check price is present (formatted as "EUR XX.XX") price_text = f"{float(item.default_price):.2f}" price_box = item_container.locator('.pretix-widget-pricebox') expect(price_box).to_contain_text(price_text) @@ -154,8 +151,8 @@ class TestWidgetEventInfo: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, + organizer, + event, widget_page ): """ @@ -165,10 +162,9 @@ class TestWidgetEventInfo: date by default. This test verifies the widget loads without checking for specific date display. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug + widget_page.goto( + live_server_url, organizer.slug, event.slug ) - widget_page.wait_for_widget_load() # Widget should be present and functional # (Event date display varies by configuration) @@ -179,8 +175,8 @@ class TestWidgetEventInfo: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, + organizer, + event, widget_page ): """ diff --git a/src/tests/e2e/test_widget_errors.py b/src/tests/e2e/test_widget_errors.py index 96ab6f72c3..7227c57824 100644 --- a/src/tests/e2e/test_widget_errors.py +++ b/src/tests/e2e/test_widget_errors.py @@ -18,16 +18,15 @@ class TestErrorDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, + organizer, + event, widget_page ): """ Loading a non-existent event should show an error message. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, 'nonexistent-event') - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, 'nonexistent-event') # Should show error message expect(page.locator( @@ -38,17 +37,16 @@ class TestErrorDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, + organizer, + event, widget_page ): """ Error state should include a link to open the ticket shop in a new tab as a fallback. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, 'nonexistent-event') - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, 'nonexistent-event') # Should show fallback action link action_link = page.locator('.pretix-widget-error-action a') @@ -66,19 +64,18 @@ class TestSoldOutState: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_sold_out, + organizer, + event, + item_sold_out, widget_page ): """ Items with zero quota should show as unavailable/sold out. """ - item = widget_item_sold_out + item = item_sold_out - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")') @@ -93,20 +90,19 @@ class TestSoldOutState: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_sold_out, + organizer, + event, + item_sold_out, widget_page ): """ Sold out items should show a status message like "Sold out" or "Currently unavailable". """ - item = widget_item_sold_out + item = item_sold_out - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")') diff --git a/src/tests/e2e/test_widget_lightbox.py b/src/tests/e2e/test_widget_lightbox.py index 66fb8f97ca..cc3424b773 100644 --- a/src/tests/e2e/test_widget_lightbox.py +++ b/src/tests/e2e/test_widget_lightbox.py @@ -19,18 +19,17 @@ class TestItemPicture: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Item with picture should display a thumbnail image.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item:has-text("{item_with_picture.name}")') expect(item_elem).to_be_visible() # Should have picture element @@ -41,18 +40,17 @@ class TestItemPicture: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Item picture should have alt text.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item:has-text("{item_with_picture.name}")') img = item_elem.locator('.pretix-widget-item-picture') alt = img.get_attribute('alt') @@ -62,18 +60,17 @@ class TestItemPicture: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Picture should be wrapped in a clickable link.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item:has-text("{item_with_picture.name}")') link = item_elem.locator('.pretix-widget-item-picture-link') expect(link).to_be_visible() @@ -84,18 +81,17 @@ class TestItemPicture: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Item row should have pretix-widget-item-with-picture class.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item-with-picture:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item-with-picture:has-text("{item_with_picture.name}")') expect(item_elem).to_be_visible() @@ -107,18 +103,17 @@ class TestLightbox: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Clicking item picture should open lightbox overlay.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item:has-text("{item_with_picture.name}")') # Click the picture link link = item_elem.locator('.pretix-widget-item-picture-link') @@ -132,18 +127,17 @@ class TestLightbox: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Lightbox should display fullsize image.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item:has-text("{item_with_picture.name}")') item_elem.locator('.pretix-widget-item-picture-link').click() # Wait for lightbox @@ -157,18 +151,17 @@ class TestLightbox: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Lightbox close button should close the overlay.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item:has-text("{item_with_picture.name}")') item_elem.locator('.pretix-widget-item-picture-link').click() # Wait for lightbox to appear @@ -187,18 +180,17 @@ class TestLightbox: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_picture, + organizer, + event, + item_with_picture, widget_page ): """Lightbox dialog should have role='alertdialog'.""" - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( - f'.pretix-widget-item:has-text("{widget_item_with_picture.name}")') + f'.pretix-widget-item:has-text("{item_with_picture.name}")') item_elem.locator('.pretix-widget-item-picture-link').click() page.wait_for_timeout(1000) diff --git a/src/tests/e2e/test_widget_loading.py b/src/tests/e2e/test_widget_loading.py index 5114c63eff..2772544eb9 100644 --- a/src/tests/e2e/test_widget_loading.py +++ b/src/tests/e2e/test_widget_loading.py @@ -19,17 +19,16 @@ class TestLoadingStates: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Loading spinner should be hidden once widget content loads. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Loading spinner should be hidden (display:none) loading = page.locator('.pretix-widget-loading') @@ -39,32 +38,32 @@ class TestLoadingStates: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Widget should fully load within 15 seconds. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) + widget_page.goto( + live_server_url, organizer.slug, event.slug, wait=False) # Widget should appear within 15s page.wait_for_selector('.pretix-widget', timeout=15000) # Items should be visible within 15s total expect(page.locator( - f'.pretix-widget-item:has-text("{widget_items[0].name}")' + f'.pretix-widget-item:has-text("{items[0].name}")' )).to_be_visible(timeout=15000) def test_no_javascript_errors_on_load( self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -73,9 +72,8 @@ class TestLoadingStates: errors = [] page.on('pageerror', lambda err: errors.append(str(err))) - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # No JS errors should have occurred assert len(errors) == 0, f"JavaScript errors: {errors}" @@ -89,18 +87,17 @@ class TestWidgetReload: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Widget CSS should load and apply styles. No SCSS syntax should leak into rendered styles. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Check that widget has actual styled dimensions # (not zero-height which would indicate CSS failure) diff --git a/src/tests/e2e/test_widget_pricing.py b/src/tests/e2e/test_widget_pricing.py index a3d64aaf76..5bfeae0893 100644 --- a/src/tests/e2e/test_widget_pricing.py +++ b/src/tests/e2e/test_widget_pricing.py @@ -20,34 +20,33 @@ class TestPriceDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Item prices should display with currency code. - Currency format should match event settings (USD). + Currency format should match event settings (EUR). """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - # Check that prices are displayed with USD currency code - # General Admission is USD 50.00 - # USD is in a separate span, so check for both parts - # Use .first since there are multiple items with USD - expect(page.locator('text=/USD/').first).to_be_visible() + # Check that prices are displayed with EUR currency code + # General Admission is EUR 50.00 + # EUR is in a separate span, so check for both parts + # Use .first since there are multiple items with EUR + expect(page.locator('text=/EUR/').first).to_be_visible() expect(page.locator('text=/50\\.00/').first).to_be_visible() def test_free_items_display_free_text( self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_free, + organizer, + event, + item_free, widget_page ): """ @@ -55,11 +54,10 @@ class TestPriceDisplay: Makes free items more obvious to users. """ - item = widget_item_free + item = item_free - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Should show "FREE" text item_elem = page.locator( @@ -73,22 +71,21 @@ class TestPriceDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_decimals, + organizer, + event, + item_with_decimals, widget_page ): """ Prices should display with proper decimal formatting. - USD should show 2 decimal places (e.g., $25.00 not $25). + EUR should show 2 decimal places (e.g., $25.00 not $25). """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Should show price with .50 - expect(page.locator('text=/USD.*12\\.50/')).to_be_visible() + expect(page.locator('text=/EUR.*12\\.50/')).to_be_visible() @pytest.mark.django_db @@ -99,9 +96,9 @@ class TestTaxDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_tax, + organizer, + event, + item_with_tax, widget_page ): """ @@ -109,11 +106,10 @@ class TestTaxDisplay: Should display "incl. X% VAT" or similar. """ - item = widget_item_with_tax + item = item_with_tax - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")') @@ -128,9 +124,9 @@ class TestTaxDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -138,13 +134,12 @@ class TestTaxDisplay: Tax line should be absent for tax-free items. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) - # widget_items by default have no tax + # items by default have no tax # We just verify the items display without errors - for item in widget_items: + for item in items: item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")') expect(item_elem).to_be_visible() @@ -158,9 +153,9 @@ class TestDiscountedPricing: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -168,18 +163,17 @@ class TestDiscountedPricing: This is a smoke test to ensure price rendering works. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # All items should display their prices - for item in widget_items: + for item in items: item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")') expect(item_elem).to_be_visible() - # Should have USD currency and price displayed - expect(item_elem.locator('text=/USD/')).to_be_visible() + # Should have EUR currency and price displayed + expect(item_elem.locator('text=/EUR/')).to_be_visible() @pytest.mark.django_db @@ -190,9 +184,9 @@ class TestPriceForVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ @@ -200,11 +194,10 @@ class TestPriceForVariations: E.g., "$20.00 - $30.00" for variations from $20 to $30. """ - item, _ = widget_item_with_variations + item, _ = item_with_variations - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Variations are collapsed by default item_elem = page.locator( @@ -212,12 +205,12 @@ class TestPriceForVariations: expect(item_elem).to_be_visible() # Should show price range in main row (not in hidden variations) - # Format: USD 20.00 – 30.00 (en-dash, not hyphen) + # Format: EUR 20.00 – 30.00 (en-dash, not hyphen) # Look specifically in the main row's price column main_row = item_elem.locator('.pretix-widget-main-item-row') price_col = main_row.locator('.pretix-widget-item-price-col') expect( - price_col.locator('text=/USD.*20\\.00/') + price_col.locator('text=/EUR.*20\\.00/') ).to_be_visible() expect(price_col.locator('text=/30\\.00/')).to_be_visible() @@ -225,9 +218,9 @@ class TestPriceForVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ @@ -235,16 +228,15 @@ class TestPriceForVariations: Each size should show its own price. """ - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Expand variations widget_page.expand_variations(item.name) - # Check each variation shows a price with USD currency + # Check each variation shows a price with EUR currency # We don't check exact amounts because formatting may vary for var in variations: var_elem = page.locator( @@ -253,5 +245,5 @@ class TestPriceForVariations: ) expect(var_elem).to_be_visible() - # Should contain USD currency code - expect(var_elem.locator('text=/USD/')).to_be_visible() + # Should contain EUR currency code + expect(var_elem.locator('text=/EUR/')).to_be_visible() diff --git a/src/tests/e2e/test_widget_quantity_controls.py b/src/tests/e2e/test_widget_quantity_controls.py index f049d3cc94..d04aa00c15 100644 --- a/src/tests/e2e/test_widget_quantity_controls.py +++ b/src/tests/e2e/test_widget_quantity_controls.py @@ -19,9 +19,9 @@ class TestQuantityControls: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_single_select, + organizer, + event, + item_single_select, widget_page ): """ @@ -30,10 +30,9 @@ class TestQuantityControls: For items limited to 1 per order, a checkbox is more intuitive than a number input. """ - item = widget_item_single_select + item = item_single_select - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Find the item item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') @@ -47,9 +46,9 @@ class TestQuantityControls: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_single_select, + organizer, + event, + item_single_select, widget_page ): """ @@ -60,10 +59,9 @@ class TestQuantityControls: Note: When there's only one item, it may be auto-selected by the widget. This test verifies the check/uncheck functionality works regardless. """ - item = widget_item_single_select + item = item_single_select - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') checkbox = item_elem.locator('input[type="checkbox"]') @@ -85,9 +83,9 @@ class TestQuantityControls: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -95,11 +93,10 @@ class TestQuantityControls: Plus/minus buttons provide an easy way to increment/decrement quantity. """ - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Regular items should have number input with +/- buttons - item_elem = page.locator(f'.pretix-widget-item:has-text("{widget_items[0].name}")') + item_elem = page.locator(f'.pretix-widget-item:has-text("{items[0].name}")') # Should have number input expect(item_elem.locator('input[type="number"]')).to_be_visible() @@ -112,9 +109,9 @@ class TestQuantityControls: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -122,10 +119,9 @@ class TestQuantityControls: Each click increments the value in the number input. """ - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) - item_elem = page.locator(f'.pretix-widget-item:has-text("{widget_items[0].name}")') + item_elem = page.locator(f'.pretix-widget-item:has-text("{items[0].name}")') number_input = item_elem.locator('input[type="number"]') plus_button = item_elem.locator('button:has-text("+")').first @@ -151,9 +147,9 @@ class TestQuantityControls: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -161,10 +157,9 @@ class TestQuantityControls: Quantity should not go below 0. """ - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) - item_elem = page.locator(f'.pretix-widget-item:has-text("{widget_items[0].name}")') + item_elem = page.locator(f'.pretix-widget-item:has-text("{items[0].name}")') number_input = item_elem.locator('input[type="number"]') plus_button = item_elem.locator('button:has-text("+")').first minus_button = item_elem.locator('button:has-text("-")').first @@ -193,9 +188,9 @@ class TestQuantityControls: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -203,14 +198,13 @@ class TestQuantityControls: Number input should accept typed values. """ - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Directly set quantity using helper - widget_page.select_item_quantity(widget_items[0].name, 5) + widget_page.select_item_quantity(items[0].name, 5) # Verify value - item_elem = page.locator(f'.pretix-widget-item:has-text("{widget_items[0].name}")') + item_elem = page.locator(f'.pretix-widget-item:has-text("{items[0].name}")') number_input = item_elem.locator('input[type="number"]') expect(number_input).to_have_value("5") @@ -223,16 +217,15 @@ class TestOrderLimits: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_single_select, + organizer, + event, + item_single_select, widget_page ): """Item with order_max=1 should show checkbox (implicit max enforcement).""" - item = widget_item_single_select # This has order_max=1 + item = item_single_select # This has order_max=1 - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') expect(item_elem).to_be_visible() @@ -246,17 +239,16 @@ class TestOrderLimits: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """Multiple items with different quantities should open iframe checkout.""" - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) - widget_page.select_item_quantity(widget_items[0].name, 2) - widget_page.select_item_quantity(widget_items[1].name, 1) + widget_page.select_item_quantity(items[0].name, 2) + widget_page.select_item_quantity(items[1].name, 1) widget_page.click_buy_button() @@ -276,9 +268,9 @@ class TestFreePrice: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_free_price, + organizer, + event, + item_free_price, widget_page ): """ @@ -286,10 +278,9 @@ class TestFreePrice: User should be able to enter their own price. """ - item = widget_item_free_price + item = item_free_price - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') expect(item_elem).to_be_visible() @@ -302,9 +293,9 @@ class TestFreePrice: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_free_price, + organizer, + event, + item_free_price, widget_page ): """ @@ -312,10 +303,9 @@ class TestFreePrice: The min attribute should be set to the item's default price. """ - item = widget_item_free_price + item = item_free_price - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') price_input = item_elem.locator('.pretix-widget-pricebox-price-input, input[name^="price_"]').first @@ -329,9 +319,9 @@ class TestFreePrice: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_free_price, + organizer, + event, + item_free_price, widget_page ): """ @@ -339,10 +329,9 @@ class TestFreePrice: Amount above minimum should be accepted. """ - item = widget_item_free_price + item = item_free_price - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') price_input = item_elem.locator('.pretix-widget-pricebox-price-input, input[name^="price_"]').first diff --git a/src/tests/e2e/test_widget_responsive.py b/src/tests/e2e/test_widget_responsive.py index 2cb0842275..3b905dcd9e 100644 --- a/src/tests/e2e/test_widget_responsive.py +++ b/src/tests/e2e/test_widget_responsive.py @@ -18,9 +18,9 @@ class TestResponsiveLayout: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -30,9 +30,8 @@ class TestResponsiveLayout: # Set narrow viewport page.set_viewport_size({"width": 375, "height": 667}) - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Should have mobile class expect(page.locator('.pretix-widget-mobile')).to_be_visible() @@ -41,9 +40,9 @@ class TestResponsiveLayout: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -53,9 +52,8 @@ class TestResponsiveLayout: # Ensure wide viewport (default is 1280) page.set_viewport_size({"width": 1280, "height": 720}) - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Should NOT have mobile class expect(page.locator('.pretix-widget-mobile')).to_have_count(0) @@ -67,9 +65,9 @@ class TestResponsiveLayout: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ @@ -79,9 +77,8 @@ class TestResponsiveLayout: # Start with desktop viewport page.set_viewport_size({"width": 1280, "height": 720}) - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Should not be mobile expect(page.locator('.pretix-widget-mobile')).to_have_count(0) diff --git a/src/tests/e2e/test_widget_variations.py b/src/tests/e2e/test_widget_variations.py index f78e010283..029f17c830 100644 --- a/src/tests/e2e/test_widget_variations.py +++ b/src/tests/e2e/test_widget_variations.py @@ -19,9 +19,9 @@ class TestProductVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ @@ -29,10 +29,9 @@ class TestProductVariations: Variations should be collapsed by default with a button to expand them. """ - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Should show item name expect(page.locator(f'text="{item.name}"')).to_be_visible() @@ -46,9 +45,9 @@ class TestProductVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ @@ -56,10 +55,9 @@ class TestProductVariations: After expanding, all variation options should be visible. """ - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Expand variations widget_page.expand_variations(item.name) @@ -75,16 +73,15 @@ class TestProductVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """Clicking toggle again should collapse variations.""" - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') toggle_btn = item_elem.locator('button:has-text("variants"), button:has-text("Show variants")') @@ -111,9 +108,9 @@ class TestProductVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ @@ -121,17 +118,16 @@ class TestProductVariations: When variations have different prices, should display range like "$20 - $30". """ - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Should show price range # Variations go from $20 to $30 item_elem = page.locator(f'.pretix-widget-item:has-text("{item.name}")') # Look for price range indicators - # Format is "USD 20.00 – 30.00" in the main item's price box (first one) + # Format is "EUR 20.00 – 30.00" in the main item's price box (first one) price_box = item_elem.locator('.pretix-widget-pricebox').first expect(price_box).to_contain_text('20.00') expect(price_box).to_contain_text('30.00') @@ -140,9 +136,9 @@ class TestProductVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ @@ -150,10 +146,9 @@ class TestProductVariations: After expanding variations, each should have its own quantity selector. """ - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Expand variations widget_page.expand_variations(item.name) @@ -171,9 +166,9 @@ class TestProductVariations: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """ @@ -181,10 +176,9 @@ class TestProductVariations: When expanded, variations should display their specific prices. """ - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Expand variations widget_page.expand_variations(item.name) @@ -216,16 +210,15 @@ class TestVariationSubmission: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_with_variations, + organizer, + event, + item_with_variations, widget_page ): """Submitting with a variation selected should open iframe checkout.""" - item, variations = widget_item_with_variations + item, variations = item_with_variations - widget_page.goto_widget_test_page(live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto(live_server_url, organizer.slug, event.slug) # Expand and select a variation widget_page.expand_variations(item.name) diff --git a/src/tests/e2e/test_widget_vouchers.py b/src/tests/e2e/test_widget_vouchers.py index 5fbde5cb4d..7b568dd32a 100644 --- a/src/tests/e2e/test_widget_vouchers.py +++ b/src/tests/e2e/test_widget_vouchers.py @@ -19,9 +19,9 @@ class TestVoucherDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_voucher, + organizer, + event, + voucher, widget_page ): """ @@ -29,9 +29,8 @@ class TestVoucherDisplay: The widget checks `vouchers_exist` in the API response. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Voucher section should be visible voucher_section = page.locator('.pretix-widget-voucher') @@ -51,17 +50,16 @@ class TestVoucherDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_items, + organizer, + event, + items, widget_page ): """ Voucher input should not appear when no vouchers exist. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Voucher section should NOT be visible voucher_section = page.locator('.pretix-widget-voucher') @@ -71,23 +69,22 @@ class TestVoucherDisplay: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_voucher, + organizer, + event, + voucher, widget_page ): """ Voucher explanation text should display when configured. """ # Set voucher explanation text on the event - widget_event.settings.set( + event.settings.set( 'voucher_explanation_text', 'Enter your voucher code to get a discount.' ) - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Explanation text should be visible explanation = page.locator('.pretix-widget-voucher-text') @@ -103,9 +100,9 @@ class TestVoucherRedemption: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_voucher, + organizer, + event, + voucher, widget_page ): """ @@ -113,9 +110,8 @@ class TestVoucherRedemption: With skip-ssl-check (added by test harness), this opens in iframe. """ - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Enter voucher code voucher_input = page.locator('.pretix-widget-voucher-input') diff --git a/src/tests/e2e/test_widget_waitinglist.py b/src/tests/e2e/test_widget_waitinglist.py index 6dfc496c09..d03cacdee0 100644 --- a/src/tests/e2e/test_widget_waitinglist.py +++ b/src/tests/e2e/test_widget_waitinglist.py @@ -17,9 +17,9 @@ class TestWaitingList: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_sold_out_with_waitinglist, + organizer, + event, + item_sold_out_with_waitinglist, widget_page ): """ @@ -31,11 +31,10 @@ class TestWaitingList: - Item: allow_waitinglist = True - Item availability < 100 (sold out) """ - item = widget_item_sold_out_with_waitinglist + item = item_sold_out_with_waitinglist - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) # Find the sold out item item_elem = page.locator( @@ -51,19 +50,18 @@ class TestWaitingList: self, page: Page, live_server_url: str, - widget_organizer, - widget_event, - widget_item_sold_out_with_waitinglist, + organizer, + event, + item_sold_out_with_waitinglist, widget_page ): """ Waiting list link URL should include item ID parameter. """ - item = widget_item_sold_out_with_waitinglist + item = item_sold_out_with_waitinglist - widget_page.goto_widget_test_page( - live_server_url, widget_organizer.slug, widget_event.slug) - widget_page.wait_for_widget_load() + widget_page.goto( + live_server_url, organizer.slug, event.slug) item_elem = page.locator( f'.pretix-widget-item:has-text("{item.name}")')