Skip to content

Commit 4e6b635

Browse files
Merge branch 'dev' into ca-fdp-changes
2 parents 54a4051 + 1ec9c09 commit 4e6b635

File tree

4 files changed

+150
-162
lines changed

4 files changed

+150
-162
lines changed

tests/e2e-test/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Installing Playwright Pytest from Virtual Environment
2323
- Install the required browsers "playwright install"
2424

2525
Run test cases
26-
- To run test cases from your 'tests' folder : "pytest --headed --html=report/report.html"
26+
- To run test cases from your 'tests/e2e-test' folder : "pytest --headed --html=report/report.html"
2727

2828
Steps need to be followed to enable Access Token and Client Credentials
2929
- Go to App Service from the resource group and select the Access Tokens check box in 'Manage->Authentication' tab

tests/e2e-test/requirements.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
pytest-playwright
22
pytest-html
3-
python-dotenv
3+
python-dotenv
4+
pytest-check
5+
pytest-reporter-html1
6+
py
7+
beautifulsoup4

tests/e2e-test/tests/conftest.py

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,98 @@
1-
from pathlib import Path
1+
from bs4 import BeautifulSoup
22
import pytest
33
from playwright.sync_api import sync_playwright
44
from config.constants import *
5-
from slugify import slugify
6-
from pages.homePage import HomePage
7-
from pages.loginPage import LoginPage
8-
from dotenv import load_dotenv
5+
import logging
6+
import atexit
97
import os
8+
import io
109

11-
10+
# Playwright session-scoped login/logout fixture
1211
@pytest.fixture(scope="session")
1312
def login_logout():
14-
# perform login and browser close once in a session
1513
with sync_playwright() as p:
1614
browser = p.chromium.launch(headless=False)
1715
context = browser.new_context()
1816
context.set_default_timeout(80000)
1917
page = context.new_page()
20-
# Navigate to the login URL
2118
page.goto(URL)
22-
# Wait for the login form to appear
2319
page.wait_for_load_state('networkidle')
2420
page.wait_for_timeout(5000)
25-
# # login to web url with username and password
21+
# Optional login steps
2622
# login_page = LoginPage(page)
2723
# load_dotenv()
2824
# login_page.authenticate(os.getenv('user_name'), os.getenv('pass_word'))
2925

3026
yield page
3127

32-
# perform close the browser
3328
browser.close()
3429

35-
30+
# Change HTML report title
3631
@pytest.hookimpl(tryfirst=True)
3732
def pytest_html_report_title(report):
3833
report.title = "Automation_BYOc_ClientAdvisor"
3934

35+
log_streams = {}
36+
37+
# Capture logs per test
38+
@pytest.hookimpl(tryfirst=True)
39+
def pytest_runtest_setup(item):
40+
stream = io.StringIO()
41+
handler = logging.StreamHandler(stream)
42+
handler.setLevel(logging.INFO)
43+
logger = logging.getLogger()
44+
logger.addHandler(handler)
45+
log_streams[item.nodeid] = (handler, stream)
4046

47+
# Add captured logs to report
4148
@pytest.hookimpl(hookwrapper=True)
4249
def pytest_runtest_makereport(item, call):
43-
pytest_html = item.config.pluginmanager.getplugin("html")
4450
outcome = yield
45-
screen_file=""
4651
report = outcome.get_result()
47-
extra = getattr(report, "extra", [])
48-
if report.when == "call":
49-
if report.failed and "page" in item.funcargs:
50-
page = item.funcargs["page"]
51-
screenshot_dir = Path("screenshots")
52-
screenshot_dir.mkdir(exist_ok=True)
53-
screen_file = str(screenshot_dir / f"{slugify(item.nodeid)}.png")
54-
page.screenshot(path=screen_file)
55-
xfail = hasattr(report, "wasxfail")
56-
if (report.skipped and xfail) or (report.failed and not xfail):
57-
# add the screenshots to the html report
58-
extra.append(pytest_html.extras.png(screen_file))
59-
report.extras = extra
52+
53+
handler, stream = log_streams.get(item.nodeid, (None, None))
54+
if handler and stream:
55+
handler.flush()
56+
log_output = stream.getvalue()
57+
logger = logging.getLogger()
58+
logger.removeHandler(handler)
59+
report.description = f"<pre>{log_output.strip()}</pre>"
60+
log_streams.pop(item.nodeid, None)
61+
else:
62+
report.description = ""
63+
64+
# Optional: simplify test display names if using `prompt`
65+
def pytest_collection_modifyitems(items):
66+
for item in items:
67+
# Retain only the readable part after the last `[` and before the closing `]`
68+
if "[" in item.nodeid and "]" in item.nodeid:
69+
pretty_name = item.nodeid.split("[", 1)[1].rsplit("]", 1)[0]
70+
item._nodeid = pretty_name
71+
else:
72+
# Use function name as fallback
73+
item._nodeid = item.name
74+
75+
76+
# Rename 'Duration' column in HTML report
77+
def rename_duration_column():
78+
report_path = os.path.abspath("report.html")
79+
if not os.path.exists(report_path):
80+
print("Report file not found, skipping column rename.")
81+
return
82+
83+
with open(report_path, 'r', encoding='utf-8') as f:
84+
soup = BeautifulSoup(f, 'html.parser')
85+
86+
headers = soup.select('table#results-table thead th')
87+
for th in headers:
88+
if th.text.strip() == 'Duration':
89+
th.string = 'Execution Time'
90+
break
91+
else:
92+
print("'Duration' column not found in report.")
93+
94+
with open(report_path, 'w', encoding='utf-8') as f:
95+
f.write(str(soup))
96+
97+
# Run after tests complete
98+
atexit.register(rename_duration_column)
Lines changed: 78 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,86 @@
1+
import logging
2+
import time
3+
import pytest
14
from config.constants import *
25
from pages.homePage import HomePage
36

7+
logger = logging.getLogger(__name__)
48

5-
# def test_chatbot_responds_with_upcoming_meeting_schedule_date(login_logout):
6-
# page = login_logout
7-
# home_page = HomePage(page)
8-
# # validate page title
9-
# assert homepage_title == page.locator(home_page.HOME_PAGE_TITLE).text_content()
10-
# # select a client
11-
# home_page.select_a_client(client_name)
12-
# # validate selected client name
13-
# assert client_name == page.locator(home_page.SELECTED_CLIENT_NAME_LABEL).text_content()
14-
# # ask a question
15-
# home_page.enter_a_question(next_meeting_question)
16-
# # click send button
17-
# home_page.click_send_button()
18-
# # Validate response status code
19-
# home_page.validate_response_status()
20-
# # validate the upcoming meeting date-time in both side panel and response
21-
# home_page.validate_next_meeting_date_time()
9+
def validate_home_and_client(home):
10+
assert homepage_title == home.page.locator(home.HOME_PAGE_TITLE).text_content()
11+
home.select_a_client(client_name)
12+
assert client_name == home.page.locator(home.SELECTED_CLIENT_NAME_LABEL).text_content()
2213

23-
def test_save_chat_confirmation_popup(login_logout):
24-
page = login_logout
25-
home_page = HomePage(page)
26-
# validate page title
27-
assert homepage_title == page.locator(home_page.HOME_PAGE_TITLE).text_content()
28-
# select a client
29-
home_page.select_a_client(client_name)
30-
# validate selected client name
31-
assert client_name == page.locator(home_page.SELECTED_CLIENT_NAME_LABEL).text_content()
32-
# clear the chat if any
33-
home_page.click_clear_chat_icon()
34-
# ask a question
35-
home_page.enter_a_question(golden_path_question1)
36-
# click send button
37-
home_page.click_send_button()
38-
# Validate response status code
39-
home_page.validate_response_status()
40-
#click on the plus button
41-
home_page.click_on_save_chat_plus_icon()
42-
assert page.locator(home_page.SAVE_CHAT_CONFIRMATION_POPUPTEXT).is_visible()
14+
def save_chat_confirmation_popup(home):
15+
home.click_clear_chat_icon()
16+
home.enter_a_question(golden_path_question1)
17+
home.click_send_button()
18+
home.validate_response_status()
19+
home.click_on_save_chat_plus_icon()
20+
assert home.page.locator(home.SAVE_CHAT_CONFIRMATION_POPUPTEXT).is_visible()
4321

44-
def test_delete_chat_history_during_response(login_logout):
45-
page = login_logout
46-
home_page = HomePage(page)
47-
# validate page title
48-
assert homepage_title == page.locator(home_page.HOME_PAGE_TITLE).text_content()
49-
# select a client
50-
home_page.select_a_client(client_name)
51-
# validate selected client name
52-
assert client_name == page.locator(home_page.SELECTED_CLIENT_NAME_LABEL).text_content()
53-
# ask a question
54-
home_page.enter_a_question(golden_path_question1)
55-
# click send button
56-
home_page.click_send_button()
57-
#click on the plus button
58-
home_page.click_on_save_chat_plus_icon()
59-
assert page.locator(home_page.SAVE_CHAT_CONFIRMATION_POPUPTEXT).is_visible()
60-
#click on show chat history button
61-
home_page.click_on_show_chat_history_button()
62-
#click on saved chat history
63-
home_page.click_on_saved_chat()
64-
#ask the question
65-
home_page.enter_a_question(golden_path_question1)
66-
#click on click_send_button_for_chat_history_response
67-
home_page.click_send_button_for_chat_history_response()
68-
# validate the delete icon disabled
69-
assert page.locator(home_page.SHOW_CHAT_HISTORY_DELETE_ICON).is_disabled()
70-
# click on hide chat history button
71-
home_page.click_hide_chat_history_button()
72-
# clear the chat
73-
home_page.click_clear_chat_icon()
74-
75-
def test_golden_path_demo_script(login_logout):
22+
def delete_chat_history_during_response(home):
23+
home.enter_a_question(golden_path_question1)
24+
home.click_send_button()
25+
home.click_on_save_chat_plus_icon()
26+
assert home.page.locator(home.SAVE_CHAT_CONFIRMATION_POPUPTEXT).is_visible()
27+
home.click_on_show_chat_history_button()
28+
home.click_on_saved_chat()
29+
home.enter_a_question(golden_path_question1)
30+
home.click_send_button_for_chat_history_response()
31+
assert home.page.locator(home.SHOW_CHAT_HISTORY_DELETE_ICON).is_disabled()
32+
home.click_hide_chat_history_button()
33+
home.click_clear_chat_icon()
34+
35+
def golden_path_full_demo(home):
36+
_validate_golden_path_response(home, golden_path_question1)
37+
_validate_golden_path_response(home, golden_path_question2)
38+
_validate_golden_path_response(home, golden_path_question3)
39+
_validate_golden_path_response(home, golden_path_question4)
40+
_validate_golden_path_response(home, golden_path_question5)
41+
_validate_client_info_absence(home, golden_path_question7)
42+
43+
# Define test steps and actions
44+
test_cases = [
45+
("Validate homepage and select client", validate_home_and_client),
46+
("Save chat confirmation popup", save_chat_confirmation_popup),
47+
("Delete chat history during response", delete_chat_history_during_response),
48+
("Golden path full demo", golden_path_full_demo),
49+
]
50+
51+
# Create readable test IDs
52+
test_ids = [f"{i+1:02d}. {desc}" for i, (desc, _) in enumerate(test_cases)]
53+
54+
def _validate_golden_path_response(home, question):
55+
home.enter_a_question(question)
56+
home.click_send_button()
57+
home.validate_response_status()
58+
response_text = home.page.locator(home.ANSWER_TEXT)
59+
assert response_text.nth(response_text.count() - 1).text_content() != invalid_response, \
60+
f"Incorrect response for question: {question}"
61+
62+
def _validate_client_info_absence(home, question):
63+
home.enter_a_question(question)
64+
home.click_send_button()
65+
home.validate_response_status()
66+
response_text = home.page.locator(home.ANSWER_TEXT).nth(home.page.locator(home.ANSWER_TEXT).count() - 1).text_content().lower()
67+
assert "arun sharma" not in response_text, "Other client information appeared in response."
68+
assert client_name.lower() not in response_text, f"Client name '{client_name}' should not be in response for question: {question}"
69+
70+
@pytest.mark.parametrize("desc, action", test_cases, ids=test_ids)
71+
def test_home_page_cases(login_logout, desc, action, request):
72+
"""
73+
Parametrized test for home page scenarios including chat flows and validations.
74+
"""
7675
page = login_logout
7776
home_page = HomePage(page)
78-
# validate page title
79-
assert homepage_title == page.locator(home_page.HOME_PAGE_TITLE).text_content()
80-
# select a client
81-
home_page.select_a_client(client_name)
82-
# validate selected client name
83-
assert client_name == page.locator(home_page.SELECTED_CLIENT_NAME_LABEL).text_content()
84-
# ask a question
85-
home_page.enter_a_question(golden_path_question1)
86-
# click send button
87-
home_page.click_send_button()
88-
# Validate response status code
89-
home_page.validate_response_status()
90-
response_text = page.locator(home_page.ANSWER_TEXT)
91-
# validate the response
92-
assert response_text.nth(response_text.count()-1).text_content() != invalid_response,"Incorrect response for question: "+golden_path_question1
93-
# ask a question
94-
home_page.enter_a_question(golden_path_question2)
95-
# click send button
96-
home_page.click_send_button()
97-
# Validate response status code
98-
home_page.validate_response_status()
99-
# validate the response
100-
assert response_text.nth(response_text.count() - 1).text_content() != invalid_response,"Incorrect response for question: "+golden_path_question2
101-
# ask a question
102-
home_page.enter_a_question(golden_path_question3)
103-
# click send button
104-
home_page.click_send_button()
105-
# Validate response status code
106-
home_page.validate_response_status()
107-
# validate the response
108-
assert response_text.nth(response_text.count() - 1).text_content() != invalid_response,"Incorrect response for question: "+golden_path_question3
109-
# ask a question
110-
home_page.enter_a_question(golden_path_question4)
111-
# click send button
112-
home_page.click_send_button()
113-
# Validate response status code
114-
home_page.validate_response_status()
115-
# validate the response
116-
assert response_text.nth(response_text.count() - 1).text_content() != invalid_response,"Incorrect response for question: "+golden_path_question4
117-
# ask a question
118-
home_page.enter_a_question(golden_path_question5)
119-
# click send button
120-
home_page.click_send_button()
121-
# Validate response status code
122-
home_page.validate_response_status()
123-
# validate the response
124-
assert response_text.nth(response_text.count() - 1).text_content() != invalid_response,"Incorrect response for question: "+golden_path_question5
125-
# # ask a question
126-
# home_page.enter_a_question(golden_path_question6)
127-
# # click send button
128-
# home_page.click_send_button()
129-
# # Validate response status code
130-
# home_page.validate_response_status()
131-
# # validate the response
132-
# assert response_text.nth(response_text.count() - 1).text_content() != invalid_response,"Incorrect response for question: "+golden_path_question6
133-
# ask a question
134-
home_page.enter_a_question(golden_path_question7)
135-
# click send button
136-
home_page.click_send_button()
137-
# Validate response status code
138-
home_page.validate_response_status()
139-
# validate the response
140-
assert (response_text.nth(response_text.count() - 1).text_content().lower()).find("arun sharma") == -1,"Other client information in response for client: "+client_name
141-
assert (response_text.nth(response_text.count() - 1).text_content().lower()).find(client_name) == -1,"Response is generated for selected client "+client_name+" even client name is different in question: "+golden_path_question7
77+
home_page.page = page # Required for locator access in helper functions
78+
logger.info(f"Running step: {desc}")
79+
80+
start = time.time()
81+
action(home_page)
82+
end = time.time()
83+
84+
duration = end - start
85+
logger.info(f"Execution Time for '{desc}': {duration:.2f}s")
86+
request.node._report_sections.append(("call", "log", f"Execution time: {duration:.2f}s"))

0 commit comments

Comments
 (0)