| """ |
| Visual regression tests with Percy integration for browser bug discovery. |
| |
| This module provides comprehensive visual regression testing across all major |
| page groups: login, dashboard, agents, canvas, and workflows. Tests use Percy |
| to capture baseline screenshots across 3 viewport sizes (mobile: 375px, tablet: 768px, |
| desktop: 1280px) for detecting CSS changes, layout shifts, and component updates. |
| |
| Total snapshots: 26 tests × 3 widths = 78+ baseline screenshots. |
| |
| Uses API-first authentication from e2e_ui fixtures for 10-100x faster setup |
| than UI-based login. |
| """ |
|
|
| import pytest |
| from playwright.sync_api import Page, expect |
|
|
| |
| try: |
| from frontend_nextjs.tests.visual.fixtures.percy_fixtures import percy_snapshot |
| PERCY_AVAILABLE = True |
| except ImportError: |
| PERCY_AVAILABLE = False |
| |
| def percy_snapshot(page, name, width=None): |
| pytest.skip(f"Percy not available (frontend_nextjs module not found). Skipped visual snapshot: {name}") |
|
|
|
|
| class TestVisualRegression: |
| """Visual regression tests across all page groups.""" |
|
|
| |
| |
| |
|
|
| @pytest.mark.visual |
| def test_visual_dashboard_default(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of default dashboard page. |
| |
| Scenarios: |
| - Navigate to dashboard (authenticated) |
| - Take Percy snapshot for baseline |
| - Verify dashboard layout elements |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/dashboard") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Dashboard - Default") |
|
|
| |
| expect(authenticated_page.locator("main, [role='main']")).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_dashboard_empty_state(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of dashboard in empty state. |
| |
| Scenarios: |
| - Check for empty state indicator |
| - Take Percy snapshot of empty state |
| - Verify empty state styling |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/dashboard") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| empty_state = authenticated_page.locator( |
| "[data-testid='empty-state'], .empty-state, .no-data" |
| ).first |
|
|
| if empty_state.count() > 0: |
| |
| percy_snapshot(authenticated_page, "Dashboard - Empty State") |
| expect(empty_state).to_be_visible() |
| else: |
| |
| percy_snapshot(authenticated_page, "Dashboard - No Empty State") |
|
|
| @pytest.mark.visual |
| def test_visual_dashboard_with_data(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of dashboard populated with data. |
| |
| Scenarios: |
| - Navigate to dashboard with existing data |
| - Take Percy snapshot of populated dashboard |
| - Verify data cards and statistics are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/dashboard") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Dashboard - With Data") |
|
|
| |
| data_cards = authenticated_page.locator( |
| "[data-testid='stats'], [data-testid='metrics'], .stat-card, .metric" |
| ) |
|
|
| if data_cards.count() > 0: |
| expect(data_cards.first).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_dashboard_sidebar_expanded(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of dashboard with expanded sidebar. |
| |
| Scenarios: |
| - Navigate to dashboard |
| - Ensure sidebar is expanded |
| - Take Percy snapshot of expanded state |
| - Verify sidebar content is visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/dashboard") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| sidebar_toggle = authenticated_page.locator( |
| "[data-testid='sidebar-toggle'], .sidebar-toggle, button[aria-label*='menu']" |
| ).first |
|
|
| if sidebar_toggle.count() > 0: |
| |
| sidebar_toggle.click() |
| authenticated_page.wait_for_timeout(500) |
|
|
| |
| percy_snapshot(authenticated_page, "Dashboard - Sidebar Expanded") |
|
|
| |
| sidebar = authenticated_page.locator("aside, nav, [data-testid='sidebar']").first |
| if sidebar.count() > 0: |
| expect(sidebar).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_dashboard_sidebar_collapsed(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of dashboard with collapsed sidebar. |
| |
| Scenarios: |
| - Navigate to dashboard |
| - Collapse sidebar |
| - Take Percy snapshot of collapsed state |
| - Verify sidebar transition works correctly |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/dashboard") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| sidebar_toggle = authenticated_page.locator( |
| "[data-testid='sidebar-toggle'], .sidebar-toggle, button[aria-label*='menu']" |
| ).first |
|
|
| if sidebar_toggle.count() > 0: |
| |
| sidebar_toggle.click() |
| authenticated_page.wait_for_timeout(500) |
|
|
| |
| percy_snapshot(authenticated_page, "Dashboard - Sidebar Collapsed") |
|
|
| |
| sidebar = authenticated_page.locator( |
| "aside.collapsed, nav.collapsed, [data-testid='sidebar'].collapsed, [data-state='collapsed']" |
| ).first |
|
|
| if sidebar.count() > 0: |
| expect(sidebar).to_be_visible() |
|
|
| |
| |
| |
|
|
| @pytest.mark.visual |
| def test_visual_agents_list(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of agents list page. |
| |
| Scenarios: |
| - Navigate to agents list |
| - Take Percy snapshot of list view |
| - Verify agent cards are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/agents") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Agents - List View") |
|
|
| |
| agent_list = authenticated_page.locator( |
| "[data-testid='agent-list'], [data-testid='agent-grid'], .agents" |
| ).first |
|
|
| if agent_list.count() > 0: |
| expect(agent_list).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_agents_grid_view(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of agents grid view. |
| |
| Scenarios: |
| - Navigate to agents list |
| - Switch to grid view (if available) |
| - Take Percy snapshot of grid view |
| - Verify grid layout is correct |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/agents") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| grid_toggle = authenticated_page.locator( |
| "[data-testid='grid-view'], button[aria-label*='grid'], .grid-view-toggle" |
| ).first |
|
|
| if grid_toggle.count() > 0: |
| grid_toggle.click() |
| authenticated_page.wait_for_timeout(500) |
|
|
| |
| percy_snapshot(authenticated_page, "Agents - Grid View") |
|
|
| |
| agent_grid = authenticated_page.locator( |
| "[data-testid='agent-grid'], .agents.grid" |
| ).first |
|
|
| if agent_grid.count() > 0: |
| expect(agent_grid).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_agent_creation_form(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of agent creation form. |
| |
| Scenarios: |
| - Navigate to agent creation page |
| - Take Percy snapshot of form |
| - Verify form fields are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/agents/new") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Agents - Creation Form") |
|
|
| |
| form = authenticated_page.locator("form, [data-testid='agent-form']").first |
| if form.count() > 0: |
| expect(form).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_agent_detail_view(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of agent detail page. |
| |
| Scenarios: |
| - Navigate to agent detail page |
| - Take Percy snapshot of detail view |
| - Verify agent information is displayed |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/agents") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| first_agent = authenticated_page.locator( |
| "[data-testid='agent-card'], a[href*='/agents/']" |
| ).first |
|
|
| if first_agent.count() > 0: |
| first_agent.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Agents - Detail View") |
|
|
| |
| detail_view = authenticated_page.locator( |
| "[data-testid='agent-detail'], .agent-detail" |
| ).first |
|
|
| if detail_view.count() > 0: |
| expect(detail_view).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_agent_execution_view(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of agent execution page. |
| |
| Scenarios: |
| - Navigate to agent execution view |
| - Take Percy snapshot of execution interface |
| - Verify execution controls are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/agents") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| execute_button = authenticated_page.locator( |
| "[data-testid='execute-agent'], button:has-text('Execute'), a:has-text('Execute')" |
| ).first |
|
|
| if execute_button.count() > 0: |
| execute_button.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Agents - Execution View") |
|
|
| |
| execution_view = authenticated_page.locator( |
| "[data-testid='execution-view'], .execution-view" |
| ).first |
|
|
| if execution_view.count() > 0: |
| expect(execution_view).to_be_visible() |
|
|
| |
| |
| |
|
|
| @pytest.mark.visual |
| def test_visual_canvas_list(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of canvas list page. |
| |
| Scenarios: |
| - Navigate to canvas list |
| - Take Percy snapshot of list |
| - Verify canvas cards are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/canvas") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Canvas - List View") |
|
|
| |
| canvas_list = authenticated_page.locator( |
| "[data-testid='canvas-list'], .canvas-list" |
| ).first |
|
|
| if canvas_list.count() > 0: |
| expect(canvas_list).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_canvas_chart_type(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of chart canvas. |
| |
| Scenarios: |
| - Navigate to chart canvas |
| - Take Percy snapshot of chart visualization |
| - Verify chart elements are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/canvas") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| chart_canvas = authenticated_page.locator( |
| "[data-testid='chart-canvas'], a:has-text('chart'), .canvas[data-type='chart']" |
| ).first |
|
|
| if chart_canvas.count() > 0: |
| chart_canvas.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Canvas - Chart Type") |
|
|
| |
| chart = authenticated_page.locator( |
| "[data-testid='chart'], canvas, .chart" |
| ).first |
|
|
| if chart.count() > 0: |
| expect(chart).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_canvas_markdown_type(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of markdown canvas. |
| |
| Scenarios: |
| - Navigate to markdown canvas |
| - Take Percy snapshot of markdown rendering |
| - Verify markdown content is visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/canvas") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| markdown_canvas = authenticated_page.locator( |
| "[data-testid='markdown-canvas'], a:has-text('markdown'), .canvas[data-type='markdown']" |
| ).first |
|
|
| if markdown_canvas.count() > 0: |
| markdown_canvas.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Canvas - Markdown Type") |
|
|
| |
| markdown = authenticated_page.locator( |
| "[data-testid='markdown'], .markdown, .prose" |
| ).first |
|
|
| if markdown.count() > 0: |
| expect(markdown).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_canvas_sheet_type(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of sheet canvas. |
| |
| Scenarios: |
| - Navigate to sheet canvas |
| - Take Percy snapshot of sheet rendering |
| - Verify sheet table is visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/canvas") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| sheet_canvas = authenticated_page.locator( |
| "[data-testid='sheet-canvas'], a:has-text('sheet'), .canvas[data-type='sheet']" |
| ).first |
|
|
| if sheet_canvas.count() > 0: |
| sheet_canvas.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Canvas - Sheet Type") |
|
|
| |
| sheet = authenticated_page.locator( |
| "[data-testid='sheet'], table, .sheet" |
| ).first |
|
|
| if sheet.count() > 0: |
| expect(sheet).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_canvas_form_type(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of form canvas. |
| |
| Scenarios: |
| - Navigate to form canvas |
| - Take Percy snapshot of form rendering |
| - Verify form fields are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/canvas") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| form_canvas = authenticated_page.locator( |
| "[data-testid='form-canvas'], a:has-text('form'), .canvas[data-type='form']" |
| ).first |
|
|
| if form_canvas.count() > 0: |
| form_canvas.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Canvas - Form Type") |
|
|
| |
| form = authenticated_page.locator( |
| "[data-testid='form'], form, .form-canvas" |
| ).first |
|
|
| if form.count() > 0: |
| expect(form).to_be_visible() |
|
|
| |
| |
| |
|
|
| @pytest.mark.visual |
| def test_visual_workflows_list(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of workflows list page. |
| |
| Scenarios: |
| - Navigate to workflows list |
| - Take Percy snapshot of list |
| - Verify workflow cards are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/workflows") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Workflows - List View") |
|
|
| |
| workflow_list = authenticated_page.locator( |
| "[data-testid='workflow-list'], .workflow-list" |
| ).first |
|
|
| if workflow_list.count() > 0: |
| expect(workflow_list).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_workflow_creation(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of workflow creation form. |
| |
| Scenarios: |
| - Navigate to workflow creation page |
| - Take Percy snapshot of creation form |
| - Verify form fields are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/workflows/new") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Workflows - Creation Form") |
|
|
| |
| form = authenticated_page.locator( |
| "[data-testid='workflow-form'], form, .workflow-creation" |
| ).first |
|
|
| if form.count() > 0: |
| expect(form).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_workflow_detail(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of workflow detail view. |
| |
| Scenarios: |
| - Navigate to workflow detail page |
| - Take Percy snapshot of detail view |
| - Verify workflow information is displayed |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/workflows") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| first_workflow = authenticated_page.locator( |
| "[data-testid='workflow-card'], a[href*='/workflows/']" |
| ).first |
|
|
| if first_workflow.count() > 0: |
| first_workflow.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Workflows - Detail View") |
|
|
| |
| detail_view = authenticated_page.locator( |
| "[data-testid='workflow-detail'], .workflow-detail" |
| ).first |
|
|
| if detail_view.count() > 0: |
| expect(detail_view).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_workflow_execution(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of workflow execution view. |
| |
| Scenarios: |
| - Navigate to workflow execution interface |
| - Take Percy snapshot of execution view |
| - Verify execution controls are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/workflows") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| execute_button = authenticated_page.locator( |
| "[data-testid='execute-workflow'], button:has-text('Execute'), a:has-text('Execute')" |
| ).first |
|
|
| if execute_button.count() > 0: |
| execute_button.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Workflows - Execution View") |
|
|
| |
| execution_view = authenticated_page.locator( |
| "[data-testid='workflow-execution'], .workflow-execution" |
| ).first |
|
|
| if execution_view.count() > 0: |
| expect(execution_view).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_workflow_automation(self, authenticated_page: Page, base_url: str): |
| """ |
| Test visual appearance of workflow automation settings. |
| |
| Scenarios: |
| - Navigate to workflow automation settings |
| - Take Percy snapshot of automation interface |
| - Verify automation controls are visible |
| |
| Args: |
| authenticated_page: Authenticated Playwright page |
| base_url: Base URL of the application |
| """ |
| |
| authenticated_page.goto(f"{base_url}/workflows") |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| automation_link = authenticated_page.locator( |
| "[data-testid='automation-settings'], a:has-text('Automation'), a:has-text('Settings')" |
| ).first |
|
|
| if automation_link.count() > 0: |
| automation_link.click() |
| authenticated_page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(authenticated_page, "Workflows - Automation Settings") |
|
|
| |
| automation_view = authenticated_page.locator( |
| "[data-testid='automation'], .automation-settings" |
| ).first |
|
|
| if automation_view.count() > 0: |
| expect(automation_view).to_be_visible() |
|
|
| |
| |
| |
|
|
| @pytest.mark.visual |
| def test_visual_login_default(self, page: Page, base_url: str): |
| """ |
| Test visual appearance of login page (no auth required). |
| |
| Scenarios: |
| - Navigate to login page |
| - Take Percy snapshot of login form |
| - Verify login form elements are visible |
| |
| Args: |
| page: Playwright page fixture (unauthenticated) |
| base_url: Base URL of the application |
| """ |
| |
| page.goto(f"{base_url}/login") |
| page.wait_for_load_state("networkidle") |
|
|
| |
| percy_snapshot(page, "Login - Default") |
|
|
| |
| expect(page.locator("input[type='email'], input[name='email']")).to_be_visible() |
| expect(page.locator("input[type='password'], input[name='password']")).to_be_visible() |
| expect(page.locator("button[type='submit']")).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_login_with_error(self, page: Page, base_url: str): |
| """ |
| Test visual appearance of login page with error state. |
| |
| Scenarios: |
| - Navigate to login page |
| - Submit invalid credentials |
| - Take Percy snapshot of error state |
| - Verify error message is displayed |
| |
| Args: |
| page: Playwright page fixture (unauthenticated) |
| base_url: Base URL of the application |
| """ |
| |
| page.goto(f"{base_url}/login") |
| page.wait_for_load_state("networkidle") |
|
|
| |
| page.fill("input[type='email'], input[name='email']", "invalid@example.com") |
| page.fill("input[type='password'], input[name='password']", "wrongpassword") |
| page.click("button[type='submit']") |
|
|
| |
| page.wait_for_timeout(1000) |
|
|
| |
| percy_snapshot(page, "Login - Error State") |
|
|
| |
| error_message = page.locator( |
| "[data-testid='error'], .error, .alert-error, [role='alert']" |
| ).first |
|
|
| if error_message.count() > 0: |
| expect(error_message).to_be_visible() |
|
|
| @pytest.mark.visual |
| def test_visual_login_loading(self, page: Page, base_url: str): |
| """ |
| Test visual appearance of login page in loading state. |
| |
| Scenarios: |
| - Navigate to login page |
| - Submit credentials and capture loading state |
| - Take Percy snapshot of loading indicator |
| - Verify loading spinner is displayed |
| |
| Args: |
| page: Playwright page fixture (unauthenticated) |
| base_url: Base URL of the application |
| """ |
| |
| page.goto(f"{base_url}/login") |
| page.wait_for_load_state("networkidle") |
|
|
| |
| page.fill("input[type='email'], input[name='email']", "test@example.com") |
| page.fill("input[type='password'], input[name='password']", "password123") |
|
|
| |
| page.click("button[type='submit']") |
|
|
| |
| page.wait_for_timeout(100) |
|
|
| |
| percy_snapshot(page, "Login - Loading State") |
|
|
| |
| loading_indicator = page.locator( |
| "[data-testid='loading'], .loading, .spinner, [role='status']" |
| ).first |
|
|
| |
| if loading_indicator.count() > 0: |
| expect(loading_indicator).to_be_visible() |
|
|