import { test, expect } from '@playwright/test'; import { testing_event_id } from './_helpers/env'; import { testing_exhibit_id, exhibit_staff_passcode, setup_leads_test_page, } from './_helpers/leads_helpers'; const event_id = testing_event_id; const exhibit_id = testing_exhibit_id; const exhibit_url = `/events/${event_id}/leads/exhibit/${exhibit_id}`; const signed_in_kv = { [exhibit_id]: { key: exhibit_staff_passcode, type: 'shared' } }; /** Locator for the CreditCard header button (Payment & Upgrades). */ const payment_btn = (page: Parameters[0]) => (page as import('@playwright/test').Page).locator( 'header button[title="Payment & Upgrades"]' ); /** Locator for the Manage / Settings header button. */ const manage_btn = (page: Parameters[0]) => (page as import('@playwright/test').Page).locator( 'header button[title="Manage Exhibit"]' ); test.describe('Leads — Payment Gate (leads_require_payment)', () => { // ----------------------------------------------------------------------- // 1. leads_require_payment = false → no CreditCard button in header // // When the event-level flag is disabled the payment tab is not surfaced // at all — the button must be absent so exhibitors cannot accidentally // navigate to the Stripe flow when the event organiser covers costs. // ----------------------------------------------------------------------- test('payment not required: CreditCard button absent from header', async ({ page }) => { await setup_leads_test_page(page, event_id, exhibit_id, { auth_kv: signed_in_kv, event_data_overrides: { mod_exhibits_json: { leads_require_payment: false }, }, }); await page.goto(exhibit_url); // Wait for the event GET to complete so we know liveQuery has fired await page.waitForResponse( (r) => r.url().includes(`crud/event/${event_id}`) && r.status() === 200, { timeout: 8_000 } ); // CreditCard button must remain absent after the event has loaded await expect(payment_btn(page)).not.toBeVisible({ timeout: 3_000 }); }); // ----------------------------------------------------------------------- // 2. leads_require_payment = true → CreditCard button visible in header // // Default mock event has leads_require_payment: true — button must appear // once the event record is written to Dexie and lq__event_obj fires. // ----------------------------------------------------------------------- test('payment required: CreditCard button visible in header', async ({ page }) => { await setup_leads_test_page(page, event_id, exhibit_id, { auth_kv: signed_in_kv, // Default mock event already has leads_require_payment: true }); await page.goto(exhibit_url); await expect(payment_btn(page)).toBeVisible({ timeout: 8_000 }); }); // ----------------------------------------------------------------------- // 3. Clicking CreditCard button opens the payment tab // // The button toggles active_tab to 'payment'. The payment component // (.ae-exhibit-payment or similar) must render inside the content area. // We verify by asserting the button switches to filled-success variant // (active tab styling) and the header primary button stays visible. // ----------------------------------------------------------------------- test('clicking CreditCard button switches to payment tab', async ({ page }) => { await setup_leads_test_page(page, event_id, exhibit_id, { auth_kv: signed_in_kv, }); await page.goto(exhibit_url); // Wait for the payment button to appear await expect(payment_btn(page)).toBeVisible({ timeout: 8_000 }); // Click it await payment_btn(page).click(); // Active tab styling: button gets preset-filled-success class await expect(payment_btn(page)).toHaveClass(/preset-filled-success/, { timeout: 3_000, }); // Primary "Add Lead / Lead List" toggle must still be visible await expect( page.locator('header button.preset-filled-primary') ).toBeVisible({ timeout: 3_000 }); }); // ----------------------------------------------------------------------- // 4. Manage tab: Billing accordion present when payment required // // When leads_require_payment is true, the Manage tab shows a collapsible // "Licenses & Billing" section so exhibitors can manage payment status. // ----------------------------------------------------------------------- test('manage tab: billing accordion visible when payment required', async ({ page }) => { await setup_leads_test_page(page, event_id, exhibit_id, { auth_kv: signed_in_kv, }); await page.goto(exhibit_url); // Wait for payment button (confirms event loaded and lq__event_obj is live) await expect(payment_btn(page)).toBeVisible({ timeout: 8_000 }); // Navigate to manage tab await manage_btn(page).click(); // Billing accordion row must be present await expect( page.locator('button:has-text("Licenses & Billing")') ).toBeVisible({ timeout: 5_000 }); }); // ----------------------------------------------------------------------- // 5. Manage tab: Billing accordion absent when payment not required // // When the flag is disabled the accordion is not rendered at all — the // conditional {#if leads_require_payment} in ae_tab__manage.svelte hides it. // ----------------------------------------------------------------------- test('manage tab: billing accordion hidden when payment not required', async ({ page }) => { await setup_leads_test_page(page, event_id, exhibit_id, { auth_kv: signed_in_kv, event_data_overrides: { mod_exhibits_json: { leads_require_payment: false }, }, }); await page.goto(exhibit_url); // Wait for event GET to complete await page.waitForResponse( (r) => r.url().includes(`crud/event/${event_id}`) && r.status() === 200, { timeout: 8_000 } ); // Navigate to manage tab await manage_btn(page).click(); // "Booth Profile" section heading confirms manage tab is rendered await expect(page.locator('text=Booth Profile')).toBeVisible({ timeout: 5_000 }); // Billing accordion must NOT appear await expect( page.locator('button:has-text("Licenses & Billing")') ).not.toBeVisible({ timeout: 3_000 }); }); });