| |
| """ |
| E2E Tests for User Journeys |
| |
| Tests that simulate realistic user journeys and scenarios: |
| - New user onboarding flow |
| - Experienced user workflows |
| - Multi-session scenarios |
| - Different use cases (M&A analyst, lawyer, consultant) |
| """ |
|
|
| import pytest |
| from playwright.sync_api import Page, expect |
| from .conftest import StreamlitPageHelpers |
|
|
|
|
| class TestUserJourneys: |
| """Test realistic user journey scenarios""" |
|
|
| def test_new_user_onboarding_journey(self, page: Page, streamlit_helpers: StreamlitPageHelpers): |
| """Test the journey of a new user discovering the application""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| expect(page.locator("h1")).to_contain_text("AI Due Diligence") |
| |
| |
| sidebar = page.locator("[data-testid='stSidebar']") |
| expect(sidebar).to_be_visible() |
| |
| |
| data_room_section = sidebar.locator("text=/.*[Dd]ata.*[Rr]oom.*/") |
| expect(data_room_section.first).to_be_visible() |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| |
| if tabs.count() >= 3: |
| |
| analysis_tab = tabs.first |
| analysis_tab.click() |
| page.wait_for_timeout(1000) |
| |
| |
| analysis_content = page.locator("text=/.*[Aa]nalysis.*|.*[Cc]ompany.*|.*[Dd]ue.*[Dd]iligence.*/") |
| |
| |
| generate_buttons = page.locator("button:has-text(/.*[Gg]enerate.*/)") |
| if generate_buttons.count() > 0: |
| generate_buttons.first.click() |
| page.wait_for_timeout(2000) |
| |
| |
| guidance_text = page.locator("text=/.*API.*key.*|.*[Cc]onfigure.*|.*[Pp]rocess.*data.*room.*/") |
| |
| |
| for i in range(1, min(tabs.count(), 4)): |
| tabs.nth(i).click() |
| page.wait_for_timeout(1000) |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
|
|
| def test_ma_analyst_workflow(self, page: Page, streamlit_helpers: StreamlitPageHelpers, sample_test_data): |
| """Test workflow of an M&A analyst conducting due diligence""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| |
| |
| |
| |
| |
| |
| sidebar = page.locator("[data-testid='stSidebar']") |
| |
| |
| path_inputs = sidebar.locator("input[type='text']") |
| if path_inputs.count() > 0 and sample_test_data["vdr_path"].exists(): |
| path_inputs.first.fill(str(sample_test_data["vdr_path"])) |
| |
| |
| process_buttons = sidebar.locator("button:has-text(/.*[Pp]rocess.*/)") |
| if process_buttons.count() > 0: |
| process_buttons.first.click() |
| page.wait_for_timeout(5000) |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| |
| |
| if tabs.count() > 0: |
| tabs.first.click() |
| page.wait_for_timeout(1000) |
| |
| generate_buttons = page.locator("button:has-text(/.*[Gg]enerate.*[Aa]nalysis.*|.*[Dd]ue.*[Dd]iligence.*/)") |
| if generate_buttons.count() > 0: |
| generate_buttons.first.click() |
| page.wait_for_timeout(5000) |
| |
| |
| checklist_tab = page.locator("button:has-text('Checklist'), text='Checklist'").first |
| if checklist_tab.count() > 0: |
| checklist_tab.click() |
| page.wait_for_timeout(1000) |
| |
| |
| process_buttons = page.locator("button:has-text(/.*[Pp]rocess.*|.*[Mm]atch.*/)") |
| if process_buttons.count() > 0: |
| process_buttons.first.click() |
| page.wait_for_timeout(3000) |
| |
| |
| export_buttons = page.locator("button:has-text(/.*[Ee]xport.*|.*[Dd]ownload.*/)") |
| download_links = page.locator("a[download]") |
| |
| export_available = export_buttons.count() > 0 or download_links.count() > 0 |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
|
|
| def test_legal_counsel_workflow(self, page: Page, streamlit_helpers: StreamlitPageHelpers): |
| """Test workflow of legal counsel reviewing due diligence items""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| |
| |
| |
| |
| |
| questions_tab = page.locator("button:has-text('Questions'), text='Questions'").first |
| if questions_tab.count() > 0: |
| questions_tab.click() |
| page.wait_for_timeout(1000) |
| |
| |
| process_buttons = page.locator("button:has-text(/.*[Pp]rocess.*|.*[Qq]uestions.*/)") |
| if process_buttons.count() > 0: |
| process_buttons.first.click() |
| page.wait_for_timeout(3000) |
| |
| |
| qa_tab = page.locator("button:has-text('Q&A'), text='Q&A'").first |
| if qa_tab.count() > 0: |
| qa_tab.click() |
| page.wait_for_timeout(1000) |
| |
| |
| question_inputs = page.locator("input[placeholder*='question'], textarea[placeholder*='question']") |
| if question_inputs.count() > 0: |
| legal_questions = [ |
| "What are the key legal risks?", |
| "Are there any pending litigations?", |
| "What intellectual property does the company own?" |
| ] |
| |
| for question in legal_questions[:1]: |
| question_inputs.first.fill(question) |
| |
| ask_buttons = page.locator("button:has-text(/.*[Aa]sk.*|.*[Ss]ubmit.*/)") |
| if ask_buttons.count() > 0: |
| ask_buttons.first.click() |
| page.wait_for_timeout(3000) |
| break |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
|
|
| def test_consultant_workflow(self, page: Page, streamlit_helpers: StreamlitPageHelpers): |
| """Test workflow of consultant conducting comprehensive analysis""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| |
| |
| |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| |
| |
| if tabs.count() > 0: |
| tabs.first.click() |
| page.wait_for_timeout(1000) |
| |
| generate_buttons = page.locator("button:has-text(/.*[Gg]enerate.*/)") |
| if generate_buttons.count() > 0: |
| generate_buttons.first.click() |
| page.wait_for_timeout(5000) |
| |
| |
| graph_tab = page.locator("button:has-text('Graph'), text='Graph'").first |
| if graph_tab.count() > 0: |
| graph_tab.click() |
| page.wait_for_timeout(1000) |
| |
| |
| graph_buttons = page.locator("button:has-text(/.*[Gg]enerate.*|.*[Bb]uild.*|.*[Ss]how.*/)") |
| if graph_buttons.count() > 0: |
| graph_buttons.first.click() |
| page.wait_for_timeout(3000) |
| |
| |
| export_buttons = page.locator("button:has-text(/.*[Ee]xport.*|.*[Cc]ombined.*|.*[Cc]omplete.*/)") |
| if export_buttons.count() > 0: |
| export_buttons.first.click() |
| page.wait_for_timeout(2000) |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
|
|
| def test_power_user_advanced_workflow(self, page: Page, streamlit_helpers: StreamlitPageHelpers, sample_test_data): |
| """Test advanced workflow of experienced power user""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| |
| |
| |
| |
| |
| |
| sidebar = page.locator("[data-testid='stSidebar']") |
| path_inputs = sidebar.locator("input[type='text']") |
| |
| if path_inputs.count() > 0 and sample_test_data["vdr_path"].exists(): |
| |
| path_inputs.first.fill(str(sample_test_data["vdr_path"])) |
| |
| process_buttons = sidebar.locator("button:has-text(/.*[Pp]rocess.*/)") |
| if process_buttons.count() > 0: |
| process_buttons.first.click() |
| page.wait_for_timeout(3000) |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| |
| if tabs.count() >= 3: |
| |
| for i in range(min(tabs.count(), 4)): |
| tabs.nth(i).click() |
| page.wait_for_timeout(500) |
| |
| |
| action_buttons = page.locator("button:has-text(/.*[Gg]enerate.*|.*[Pp]rocess.*|.*[Aa]nalyze.*/)") |
| if action_buttons.count() > 0: |
| action_buttons.first.click() |
| page.wait_for_timeout(1000) |
| |
| |
| qa_tab = page.locator("button:has-text('Q&A'), text='Q&A'").first |
| if qa_tab.count() > 0: |
| qa_tab.click() |
| page.wait_for_timeout(500) |
| |
| question_inputs = page.locator("input[placeholder*='question'], textarea[placeholder*='question']") |
| if question_inputs.count() > 0: |
| |
| advanced_question = "Provide a detailed risk assessment including financial, operational, and strategic risks with specific citations from the documents" |
| question_inputs.first.fill(advanced_question) |
| |
| ask_buttons = page.locator("button:has-text(/.*[Aa]sk.*/)") |
| if ask_buttons.count() > 0: |
| ask_buttons.first.click() |
| page.wait_for_timeout(2000) |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
|
|
| def test_multi_session_continuity(self, page: Page, streamlit_helpers: StreamlitPageHelpers): |
| """Test that user can effectively work across multiple sessions""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| |
| sidebar = page.locator("[data-testid='stSidebar']") |
| text_inputs = sidebar.locator("input[type='text']") |
| |
| if text_inputs.count() > 0: |
| test_path = "/test/session/continuity" |
| text_inputs.first.fill(test_path) |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| if tabs.count() > 0: |
| tabs.first.click() |
| page.wait_for_timeout(1000) |
| |
| |
| page.reload() |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
| expect(page.locator("[data-testid='stSidebar']")).to_be_visible() |
| |
| |
| sidebar_after_reload = page.locator("[data-testid='stSidebar']") |
| expect(sidebar_after_reload).to_be_visible() |
| |
| |
| tabs_after_reload = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| if tabs_after_reload.count() > 0: |
| tabs_after_reload.first.click() |
| page.wait_for_timeout(1000) |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
|
|
| def test_error_recovery_user_journey(self, page: Page, streamlit_helpers: StreamlitPageHelpers): |
| """Test user journey when encountering and recovering from errors""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| sidebar = page.locator("[data-testid='stSidebar']") |
| path_inputs = sidebar.locator("input[type='text']") |
| |
| if path_inputs.count() > 0: |
| |
| path_inputs.first.fill("/completely/invalid/path") |
| |
| process_buttons = sidebar.locator("button:has-text(/.*[Pp]rocess.*/)") |
| if process_buttons.count() > 0: |
| process_buttons.first.click() |
| page.wait_for_timeout(3000) |
| |
| |
| error_elements = page.locator(".stError, [data-testid='stError'], text=/.*[Ee]rror.*|.*[Nn]ot found.*/") |
| |
| |
| path_inputs.first.clear() |
| path_inputs.first.fill("") |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| if tabs.count() > 0: |
| tabs.first.click() |
| page.wait_for_timeout(1000) |
| |
| generate_buttons = page.locator("button:has-text(/.*[Gg]enerate.*/)") |
| if generate_buttons.count() > 0: |
| generate_buttons.first.click() |
| page.wait_for_timeout(2000) |
| |
| |
| api_error = page.locator("text=/.*API.*key.*|.*[Cc]onfigure.*AI.*/") |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
| |
| |
| if tabs.count() > 1: |
| tabs.nth(1).click() |
| page.wait_for_timeout(1000) |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
|
|
| def test_accessibility_focused_user_journey(self, page: Page, streamlit_helpers: StreamlitPageHelpers): |
| """Test user journey with focus on accessibility""" |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| |
| first_button = page.locator("button").first |
| if first_button.count() > 0: |
| first_button.focus() |
| expect(first_button).to_be_focused() |
| |
| |
| page.keyboard.press("Tab") |
| page.wait_for_timeout(500) |
| |
| |
| focused_element = page.locator(":focus") |
| expect(focused_element).to_have_count(1) |
| |
| |
| main_content = page.locator("main, [role='main']") |
| expect(main_content).to_be_visible() |
| |
| |
| sidebar = page.locator("[data-testid='stSidebar']") |
| expect(sidebar).to_be_visible() |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| if tabs.count() > 0: |
| for i in range(min(tabs.count(), 3)): |
| tab = tabs.nth(i) |
| |
| tab_text = tab.inner_text() |
| assert len(tab_text) > 0, f"Tab {i} should have accessible text" |
|
|
| def test_mobile_user_journey(self, page: Page, streamlit_helpers: StreamlitPageHelpers): |
| """Test user journey on mobile device""" |
| |
| page.set_viewport_size({"width": 375, "height": 667}) |
| streamlit_helpers.wait_for_streamlit_load() |
| |
| |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
| |
| |
| sidebar = page.locator("[data-testid='stSidebar']") |
| |
| |
| if not sidebar.is_visible(): |
| menu_buttons = page.locator("button:has-text('☰'), button[aria-label*='menu']") |
| if menu_buttons.count() > 0: |
| menu_buttons.first.click() |
| page.wait_for_timeout(1000) |
| |
| |
| tabs = page.locator("[data-testid='stTabs'] button, .stTabs button") |
| if tabs.count() > 0: |
| |
| tabs.first.click() |
| page.wait_for_timeout(1000) |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
| |
| |
| buttons = page.locator("button") |
| if buttons.count() > 0: |
| |
| buttons.first.click() |
| page.wait_for_timeout(1000) |
| expect(page.locator("[data-testid='stApp']")).to_be_visible() |
| |
| |
| page.set_viewport_size({"width": 1280, "height": 720}) |
|
|