Skip to content

Commit fedee97

Browse files
test: Implement Log execution time per prompt in Report for CWYD (#1832)
1 parent eccd758 commit fedee97

File tree

7 files changed

+234
-171
lines changed

7 files changed

+234
-171
lines changed

tests/e2e-test/pages/adminPage.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ def __init__(self, page):
2828
def click_delete_data_tab(self):
2929
self.page.locator(self.DELETE_DATA_TAB).click()
3030
self.page.wait_for_timeout(5000)
31+
32+
def assert_admin_page_title(self, admin_page):
33+
actual_title = self.page.locator(admin_page.ADMIN_PAGE_TITLE).text_content()
34+
expected_title = admin_page.ADMIN_PAGE_TITLE
35+
assert expected_title == actual_title, f"Expected title: {expected_title}, Found: {actual_title}"

tests/e2e-test/pages/webUserPage.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
from asyncio.log import logger
12
from base.base import BasePage
23
from playwright.sync_api import expect
3-
4-
54
class WebUserPage(BasePage):
65
WEB_PAGE_TITLE = "//h3[text()='Azure AI']"
76
TYPE_QUESTION_TEXT_AREA = "//textarea[contains(@placeholder,'Type a new question')]"
@@ -22,6 +21,9 @@ class WebUserPage(BasePage):
2221
TOGGLE_CITATIONS_LIST = "[data-testid='toggle-citations-list']"
2322
CITATIONS_CONTAINER = "[data-testid='citations-container']"
2423
CITATION_BLOCK = "[data-testid='citation-block']"
24+
SHOW_CHAT_HISTORY_BUTTON="//span[text()='Show Chat History']"
25+
HIDE_CHAT_HISTORY_BUTTON = "//span[text()='Hide Chat History']"
26+
CHAT_HISTORY_ITEM = "//div[@aria-label='chat history item']"
2527

2628
def __init__(self, page):
2729
self.page = page
@@ -53,24 +55,37 @@ def click_clear_chat_icon(self):
5355
self.page.locator(self.CLEAR_CHAT_ICON).click()
5456

5557
def show_chat_history(self):
56-
self.page.locator(self.SHOW_CHAT_HISTORY).click()
57-
self.page.wait_for_load_state("networkidle")
58-
self.page.wait_for_timeout(2000)
59-
expect(self.page.locator(self.CHAT_HISTORY_NAME)).to_be_visible()
58+
"""Click to show chat history if the button is visible."""
59+
show_button = self.page.locator(self.SHOW_CHAT_HISTORY_BUTTON)
60+
if show_button.is_visible():
61+
show_button.click()
62+
self.page.wait_for_timeout(2000)
63+
expect(self.page.locator(self.CHAT_HISTORY_ITEM)).to_be_visible()
64+
else:
65+
logger.info("'Show' button not visible — chat history may already be shown.")
66+
67+
# def show_chat_history(self):
68+
# self.page.wait_for_selector(self.SHOW_CHAT_HISTORY_BUTTON)
69+
# self.page.locator(self.SHOW_CHAT_HISTORY_BUTTON).click()
70+
# self.page.wait_for_timeout(1000)
6071

6172
def close_chat_history(self):
62-
self.page.locator(self.CHAT_CLOSE_ICON).click()
63-
self.page.wait_for_load_state("networkidle")
64-
self.page.wait_for_timeout(2000)
73+
"""Click to close chat history if visible."""
74+
hide_button = self.page.locator(self.HIDE_CHAT_HISTORY_BUTTON)
75+
if hide_button.is_visible():
76+
hide_button.click()
77+
self.page.wait_for_timeout(2000)
78+
else:
79+
logger.info("Hide button not visible. Chat history might already be closed.")
6580

6681
def delete_chat_history(self):
6782
self.page.locator(self.SHOW_CHAT_HISTORY).click()
6883
self.page.wait_for_timeout(2000)
6984
chat_history = self.page.locator("//span[contains(text(),'No chat history.')]")
7085
if chat_history.is_visible():
7186
self.page.wait_for_load_state("networkidle")
72-
self.page.wait_for_timeout(2000)
73-
self.page.get_by_label("hide button").click()
87+
self.page.locator("button[title='Hide']").wait_for(state="visible", timeout=5000)
88+
self.page.locator("button[title='Hide']").click()
7489

7590
else:
7691
self.page.locator(self.CHAT_HISTORY_OPTIONS).click()

tests/e2e-test/pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ log_cli = true
33
log_cli_level = INFO
44
log_file = logs/tests.log
55
log_file_level = INFO
6-
addopts = -p no:warnings
6+
addopts = -p no:warnings --tb=short

tests/e2e-test/requirements.txt

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

tests/e2e-test/tests/conftest.py

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,92 @@
1-
import os
2-
31
import pytest
4-
from config.constants import *
2+
import os
3+
import io
4+
import logging
5+
import atexit
6+
from bs4 import BeautifulSoup
57
from playwright.sync_api import sync_playwright
6-
from py.xml import html # type: ignore
8+
from config.constants import *
79

10+
log_streams = {}
811

12+
# ---------- FIXTURE: Login and Logout Setup ----------
913
@pytest.fixture(scope="session")
1014
def login_logout():
11-
# perform login and browser close once in a session
1215
with sync_playwright() as p:
1316
browser = p.chromium.launch(headless=False, args=["--start-maximized"])
1417
context = browser.new_context(no_viewport=True)
1518
context.set_default_timeout(80000)
1619
page = context.new_page()
17-
# Navigate to the login URL
20+
21+
# Load URL and wait
1822
page.goto(WEB_URL)
19-
# Wait for the login form to appear
2023
page.wait_for_load_state("networkidle")
2124
page.wait_for_timeout(5000)
22-
# login to web url with username and password
23-
# login_page = LoginPage(page)
25+
26+
# Uncomment if authentication is needed
2427
# load_dotenv()
28+
# login_page = LoginPage(page)
2529
# login_page.authenticate(os.getenv('user_name'), os.getenv('pass_word'))
30+
2631
yield page
2732
browser.close()
2833

29-
34+
# ---------- HTML Report Title ----------
3035
@pytest.hookimpl(tryfirst=True)
3136
def pytest_html_report_title(report):
3237
report.title = "Test_Automation_Chat_with_your_Data"
3338

39+
# ---------- Logging Setup per Test ----------
40+
@pytest.hookimpl(tryfirst=True)
41+
def pytest_runtest_setup(item):
42+
stream = io.StringIO()
43+
handler = logging.StreamHandler(stream)
44+
handler.setLevel(logging.INFO)
45+
logger = logging.getLogger()
46+
logger.addHandler(handler)
47+
log_streams[item.nodeid] = (handler, stream)
3448

35-
# Add a column for descriptions
36-
def pytest_html_results_table_header(cells):
37-
cells.insert(1, html.th("Description"))
38-
39-
40-
def pytest_html_results_table_row(report, cells):
41-
cells.insert(
42-
1, html.td(report.description if hasattr(report, "description") else "")
43-
)
44-
45-
46-
# Add logs and docstring to report
49+
# ---------- Attach Logs to HTML Report ----------
4750
@pytest.hookimpl(hookwrapper=True)
4851
def pytest_runtest_makereport(item, call):
4952
outcome = yield
5053
report = outcome.get_result()
51-
report.description = str(item.function.__doc__)
52-
os.makedirs("logs", exist_ok=True)
53-
extra = getattr(report, "extra", [])
54-
report.extra = extra
54+
55+
if report.when == "call":
56+
question_logs = getattr(item, "_question_logs", None)
57+
if question_logs:
58+
for i, (question, logs) in enumerate(question_logs.items(), start=1):
59+
report.sections.append((f"Q{i:02d}: {question}", logs))
60+
else:
61+
log = getattr(item, "_captured_log", None)
62+
if log:
63+
report.sections.append(("Captured Log", log))
64+
65+
# ---------- Optional: Clean Up Node IDs for Parametrized Prompts ----------
66+
def pytest_collection_modifyitems(items):
67+
for item in items:
68+
if hasattr(item, 'callspec') and "prompt" in item.callspec.params:
69+
item._nodeid = item.callspec.params["prompt"]
70+
71+
# ---------- Rename Duration Column in HTML Report ----------
72+
def rename_duration_column():
73+
report_path = os.path.abspath("report.html")
74+
if not os.path.exists(report_path):
75+
print("Report file not found, skipping column rename.")
76+
return
77+
78+
with open(report_path, 'r', encoding='utf-8') as f:
79+
soup = BeautifulSoup(f, 'html.parser')
80+
81+
headers = soup.select('table#results-table thead th')
82+
for th in headers:
83+
if th.text.strip() == 'Duration':
84+
th.string = 'Execution Time'
85+
break
86+
else:
87+
print("'Duration' column not found in report.")
88+
89+
with open(report_path, 'w', encoding='utf-8') as f:
90+
f.write(str(soup))
91+
92+
atexit.register(rename_duration_column)

0 commit comments

Comments
 (0)