Skip to content

Commit ff4e5b7

Browse files
2 parents bd3f1b4 + c07fcd7 commit ff4e5b7

File tree

7 files changed

+172
-64
lines changed

7 files changed

+172
-64
lines changed

docs/DeploymentGuide.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,16 @@ Once you've opened the project in [Codespaces](#github-codespaces), [Dev Contain
173173
- This deployment will take *4-6 minutes* to provision the resources in your account and set up the solution with sample data.
174174
- If you encounter an error or timeout during deployment, changing the location may help, as there could be availability constraints for the resources.
175175
176-
5. Once the deployment has completed successfully, open the [Azure Portal](https://portal.azure.com/), go to the deployed resource group, find the App Service, and get the app URL from `Default domain`.
176+
5. Once the deployment has completed successfully:
177+
> Please check the terminal or console output for details of the successful deployment. It will display the Name, Endpoint (Application URL), and Azure Portal URL for both the Web and API Azure Container Apps.
177178
178-
6. If you are done trying out the application, you can delete the resources by running `azd down`.
179+
![](./images/cp-post-deployment.png)
180+
181+
- You can find the Azure portal link in the screenshot above. Click on it to navigate to the corresponding resource group in the Azure portal.
182+
183+
> #### Important Note : Before accessing the application, ensure that all **[Post Deployment Steps](#post-deployment-steps)** are fully completed, as they are critical for the proper configuration of **Data Ingestion** and **Authentication** functionalities.
184+
185+
7. If you are done trying out the application, you can delete the resources by running `azd down`.
179186
180187
### Publishing Local Build Container to Azure Container Registry
181188

docs/images/cp-post-deployment.png

178 KB
Loading

tests/e2e-test/pytest.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ 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
7+

tests/e2e-test/readme.MD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Installing Playwright Pytest from Virtual Environment
2424

2525
Run test cases
2626

27-
- To run test cases from your 'tests' folder : "pytest --html=report.html --self-contained-html"
27+
- To run test cases from your 'tests/e2e-test' folder : "pytest --html=report.html --self-contained-html"
2828

2929
Create .env file in project root level with web app url and client credentials
3030

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: 94 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,121 @@
1-
import os
1+
"""
2+
Pytest configuration for browser-based testing with Playwright and HTML report customization.
3+
"""
4+
5+
import io
6+
import atexit
7+
import logging
8+
from pathlib import Path
9+
from venv import logger
210

311
import pytest
4-
from config.constants import URL
12+
from bs4 import BeautifulSoup
513
from playwright.sync_api import sync_playwright
6-
from py.xml import html # type: ignore
14+
15+
from config.constants import URL
16+
17+
# Global dictionary to store log streams for each test
18+
LOG_STREAMS = {}
719

820

921
@pytest.fixture(scope="session")
1022
def login_logout():
11-
# perform login and browser close once in a session
12-
with sync_playwright() as p:
13-
browser = p.chromium.launch(headless=False, args=["--start-maximized"])
23+
"""
24+
Fixture to launch the browser, log in, and yield the page object.
25+
Closes the browser after the session ends.
26+
"""
27+
with sync_playwright() as playwright:
28+
browser = playwright.chromium.launch(headless=False, args=["--start-maximized"])
1429
context = browser.new_context(no_viewport=True)
1530
context.set_default_timeout(80000)
1631
page = context.new_page()
17-
# Navigate to the login URL
32+
1833
page.goto(URL, wait_until="domcontentloaded")
19-
# login to web url with username and password
34+
35+
# Uncomment and complete the following to enable login
2036
# login_page = LoginPage(page)
2137
# load_dotenv()
22-
# login_page.authenticate(os.getenv('user_name'), os.getenv('pass_word'))
38+
# login_page.authenticate(os.getenv("user_name"), os.getenv("pass_word"))
2339

2440
yield page
25-
# perform close the browser
41+
2642
browser.close()
2743

2844

2945
@pytest.hookimpl(tryfirst=True)
30-
def pytest_html_report_title(report):
31-
report.title = "Automation_Content_Processing"
32-
46+
def pytest_runtest_setup(item):
47+
"""
48+
Pytest hook to set up a log capture for each test.
49+
"""
50+
stream = io.StringIO()
51+
handler = logging.StreamHandler(stream)
52+
handler.setLevel(logging.INFO)
3353

34-
# Add a column for descriptions
35-
def pytest_html_results_table_header(cells):
36-
cells.insert(1, html.th("Description"))
54+
logger = logging.getLogger()
55+
logger.addHandler(handler)
3756

57+
LOG_STREAMS[item.nodeid] = (handler, stream)
3858

39-
def pytest_html_results_table_row(report, cells):
40-
cells.insert(
41-
1, html.td(report.description if hasattr(report, "description") else "")
42-
)
4359

44-
45-
# Add logs and docstring to report
4660
@pytest.hookimpl(hookwrapper=True)
4761
def pytest_runtest_makereport(item, call):
62+
"""
63+
Pytest hook to add captured logs to the test report.
64+
"""
4865
outcome = yield
4966
report = outcome.get_result()
50-
report.description = str(item.function.__doc__)
51-
os.makedirs("logs", exist_ok=True)
52-
extra = getattr(report, "extra", [])
53-
report.extra = extra
67+
68+
handler, stream = LOG_STREAMS.get(item.nodeid, (None, None))
69+
70+
if handler and stream:
71+
handler.flush()
72+
log_output = stream.getvalue()
73+
74+
logger = logging.getLogger()
75+
logger.removeHandler(handler)
76+
77+
report.description = f"<pre>{log_output.strip()}</pre>"
78+
79+
LOG_STREAMS.pop(item.nodeid, None)
80+
else:
81+
report.description = ""
82+
83+
84+
def pytest_collection_modifyitems(items):
85+
"""
86+
Modify test node IDs based on the test's parameterized 'prompt' value.
87+
"""
88+
for item in items:
89+
if hasattr(item, "callspec"):
90+
prompt = item.callspec.params.get("prompt")
91+
if prompt:
92+
item._nodeid = prompt
93+
94+
95+
def rename_duration_column():
96+
"""
97+
Modify the HTML report to rename 'Duration' column to 'Execution Time'.
98+
Runs automatically after the test session.
99+
"""
100+
report_path = Path("report.html")
101+
if not report_path.exists():
102+
logger.info("Report file not found, skipping column rename.")
103+
return
104+
105+
with report_path.open("r", encoding="utf-8") as file:
106+
soup = BeautifulSoup(file, "html.parser")
107+
108+
headers = soup.select("table#results-table thead th")
109+
for th in headers:
110+
if th.text.strip() == "Duration":
111+
th.string = "Execution Time"
112+
break
113+
else:
114+
print("'Duration' column not found in report.")
115+
116+
with report_path.open("w", encoding="utf-8") as file:
117+
file.write(str(soup))
118+
119+
120+
# Register HTML report column modification
121+
atexit.register(rename_duration_column)
Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,72 @@
11
import logging
2-
2+
import time
33
import pytest
44
from pages.HomePage import HomePage
55

66
logger = logging.getLogger(__name__)
77

8+
# Define step-wise test actions for Golden Path
9+
golden_path_steps = [
10+
("Validate home page is loaded", lambda home: home.validate_home_page()),
11+
("Select Invoice Schema", lambda home: home.select_schema("Invoice")),
12+
("Upload Invoice documents", lambda home: home.upload_files("Invoice")),
13+
("Refreshing the page until the 'Invoice' file status is updated to 'Completed'", lambda home: home.refresh()),
14+
(
15+
"Validate extracted result for Invoice",
16+
lambda home: home.validate_invoice_extracted_result(),
17+
),
18+
(
19+
"Modify Extracted Data JSON & submit comments",
20+
lambda home: home.modify_and_submit_extracted_data(),
21+
),
22+
("Validate process steps for Invoice", lambda home: home.validate_process_steps()),
23+
(
24+
"Select Property Loss Damage Claim Form Schema",
25+
lambda home: home.select_schema("Property"),
26+
),
27+
(
28+
"Upload Property Loss Damage Claim Form documents",
29+
lambda home: home.upload_files("Property"),
30+
),
31+
("Refreshing the page until the 'Claim Form' status is updated to 'Completed'", lambda home: home.refresh()),
32+
(
33+
"Validate extracted result for Property Loss Damage Claim Form",
34+
lambda home: home.validate_property_extracted_result(),
35+
),
36+
(
37+
"Validate process steps for Property Loss Damage Claim Form",
38+
lambda home: home.validate_process_steps(),
39+
),
40+
("Validate user able to delete file", lambda home: home.delete_files()),
41+
]
42+
43+
# Generate readable test step IDs
44+
golden_path_ids = [
45+
f"{i+1:02d}. {desc}" for i, (desc, _) in enumerate(golden_path_steps)
46+
]
47+
848

9-
@pytest.mark.testcase_id("TC001")
10-
def test_ContentProcessing_Golden_path_test(login_logout):
11-
"""Validate Golden path test case for Content Processing Accelerator"""
49+
@pytest.mark.parametrize("description, action", golden_path_steps, ids=golden_path_ids)
50+
def test_content_processing_steps(login_logout, description, action, request):
51+
"""
52+
Executes Golden Path content processing steps with individual log entries.
53+
"""
54+
request.node._nodeid = description
1255
page = login_logout
13-
home_page = HomePage(page)
14-
logger.info("Step 1: Validate home page is loaded.")
15-
home_page.validate_home_page()
16-
logger.info("Step 2: Select Invoice Schema.")
17-
home_page.select_schema("Invoice")
18-
logger.info("Step 3: Upload Invoice documents.")
19-
home_page.upload_files("Invoice")
20-
logger.info("Step 4: Refresh page till status is updated to Completed.")
21-
home_page.refresh()
22-
logger.info("Step 5: Validate extracted result for Invoice.")
23-
home_page.validate_invoice_extracted_result()
24-
logger.info("Step 6: Modify Extracted Data JSON & submit comments.")
25-
home_page.modify_and_submit_extracted_data()
26-
logger.info("Step 7: Validate process steps for Invoice")
27-
home_page.validate_process_steps()
28-
logger.info("Step 8: Select Property Loss Damage Claim Form Schema.")
29-
home_page.select_schema("Property")
30-
logger.info("Step 9: Upload Property Loss Damage Claim Form documents.")
31-
home_page.upload_files("Property")
32-
logger.info("Step 10: Refresh page till status is updated to Completed.")
33-
home_page.refresh()
34-
logger.info(
35-
"Step 11: Validate extracted result for Property Loss Damage Claim Form."
36-
)
37-
home_page.validate_property_extracted_result()
38-
logger.info("Step 12: Validate process steps for Property Loss Damage Claim Form.")
39-
home_page.validate_process_steps()
40-
logger.info("Step 13: Validate Delete files.")
41-
home_page.delete_files()
56+
home = HomePage(page)
57+
58+
logger.info(f"Running test step: {description}")
59+
60+
start_time = time.time()
61+
try:
62+
action(home)
63+
duration = time.time() - start_time
64+
message = "Step passed: %s (Duration: %.2f seconds)" % (description, duration)
65+
logger.info(message)
66+
request.node._report_sections.append(("call", "log", message))
67+
68+
except Exception:
69+
duration = time.time() - start_time
70+
logger.error("Step failed: %s (Duration: %.2f seconds)", description, duration, exc_info=True)
71+
raise
72+
request.node._report_sections.append(("call", "log", f"Step passed: {description}"))

0 commit comments

Comments
 (0)