Skip to content

Commit 93751f0

Browse files
committed
first commit
0 parents  commit 93751f0

34 files changed

+673
-0
lines changed

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
# Setup
3+
1. Execute `pip3 install -r requirements.tx`
4+
2. Set up allure-report
5+
```
6+
Download from https://allurereport.org/docs/install-for-linux/
7+
Execute: sudo dpkg -i allure_*_all.deb
8+
Execute: allure --version
9+
```
10+
3. Execute `playwright install`
11+
4. Setup `resources/details.json` as
12+
```
13+
# TO DO
14+
```
15+
16+
# Start Tests
17+
1. Execute `python3 runner.py`
18+
19+
20+
# Helpers
21+
1. Check all behave options `behave -h`
22+
2. Check all allure options `allure -h`
23+
3. Check all playwright options `playwright -h`
24+
4Execute `allure serve FOLDER_PATH` to start and create allure-report
25+
26+
# Notes:
27+
1. Test will be executed in parallel feature by feature
28+
2. Create Gmail Key Password
29+
```
30+
1. Goto: https://myaccount.google.com/apppasswords
31+
2. Enter App Name
32+
3. Copy generated password
33+
4. Provide in resources/details.json
34+
```

ToDo.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Playwright in docker - https://chatgpt.com/c/6711edb3-49b8-8010-9a2e-933316f2542e
2+
3+
Fix allure report for parallel run

behave.ini

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[behave]
2+
format = pretty
3+
json.pretty
4+
rerun
5+
allure_behave.formatter:AllureFormatter
6+
7+
outfiles = reports/pretty/pretty.txt
8+
reports/json/report.json
9+
reports/rerun/rerun.txt
10+
reports/allure_json
11+
12+
stderr_capture = True
13+
stdout_capture = True
14+
15+
log_capture = True
16+
logging_level = INFO
17+
logging_format = LOG.%(levelname)-8s %(asctime)s %(name)-10s: %(message)s
18+
19+
verbose = True
20+
;tags = @ss
21+
paths = tests/features/
22+
color = True
23+
dry_run = False
24+
show_timings = True
25+
show_skipped = false
26+
27+
# Define the output for JUnit reports (optional)
28+
;junit = True
29+
;junit_directory = reports/xml

conf_behavex.cfg

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[output]
2+
path = "reports/html"

helpers/__init__.py

Whitespace-only changes.

helpers/constants/__init__.py

Whitespace-only changes.
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
import os.path
3+
4+
5+
class FrameworkConstants:
6+
7+
reports_parent_dir = os.path.abspath("reports")
8+
allure_json_dir = os.path.abspath(f"{reports_parent_dir}/allure_json")
9+
allure_html_dir = os.path.abspath(f"{reports_parent_dir}/allure_html")
10+
html_dir = os.path.abspath(f"{reports_parent_dir}/html")
11+
logs_dir = os.path.abspath(f"{reports_parent_dir}/logs")
12+
json_dir = os.path.abspath(f"{reports_parent_dir}/json")
13+
pretty_dir = os.path.abspath(f"{reports_parent_dir}/pretty")
14+
rerun_dir = os.path.abspath(f"{reports_parent_dir}/rerun")
15+
screenshots_dir = os.path.abspath(f"{reports_parent_dir}/screenshots")
16+
test_trace_dir = os.path.abspath(f"{reports_parent_dir}/test_traces")
17+
18+
resources = os.path.abspath("resources")
19+
20+
docker_compose_file = os.path.abspath("resources/docker-compose.yml")
21+
details_file = os.path.abspath(f"{resources}/details.json")
22+
behave_ini = os.path.abspath("behave.ini")
23+
conf_behavex = os.path.abspath("conf_behavex.cfg")
24+
25+
features = os.path.abspath("tests/features")

helpers/constants/test_constants.py

Whitespace-only changes.

helpers/variables/__init__.py

Whitespace-only changes.

requirements.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
behave~=1.2.6
2+
allure-behave
3+
behavex
4+
allure-python-commons~=2.13.5
5+
Faker
6+
pycommons-lang
7+
playwright

resources/details.json

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"delete_old_reports": true,
3+
"start_docker_compose": true,
4+
"password_for_sshpass": "password",
5+
"headless": false,
6+
"allow_tracing": true,
7+
"email": {
8+
"send_report_on_email": false,
9+
"token": "",
10+
"sender_email": "",
11+
"receiver_email": ""
12+
},
13+
14+
"selenium_host_ip": "127.0.0.1",
15+
"browser": "Chrome",
16+
17+
"url": "https://www.saucedemo.com",
18+
"logins": {
19+
"standard_user": {
20+
"username": "standard_user",
21+
"password": "secret_sauce"
22+
},
23+
"locked_out_user": {
24+
"username": "locked_out_user",
25+
"password": "secret_sauce"
26+
},
27+
"performance_glitch_user": {
28+
"username": "performance_glitch_user",
29+
"password": "secret_sauce"
30+
},
31+
"error_user": {
32+
"username": "error_user",
33+
"password": "secret_sauce"
34+
},
35+
"problem_user": {
36+
"username": "problem_user",
37+
"password": "secret_sauce"
38+
}
39+
}
40+
}

resources/docker-compose.yml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# To execute this docker compose yml file use `docker compose -f docker-compose-v2.yml up`
2+
# Add the `-d` flag at the end for detached execution
3+
# To stop the execution, hit Ctrl+C, and then `docker compose -f docker-compose-v2.yml down`
4+
version: '2'
5+
services:
6+
chrome:
7+
image: selenium/node-chrome:4.25.0-20241010
8+
shm_size: 2gb
9+
depends_on:
10+
- selenium-hub
11+
environment:
12+
- SE_EVENT_BUS_HOST=selenium-hub
13+
- SE_EVENT_BUS_PUBLISH_PORT=4442
14+
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
15+
- SE_NODE_MAX_SESSIONS=3
16+
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
17+
ports:
18+
- "6900:5900"
19+
20+
firefox:
21+
image: selenium/node-firefox:4.25.0-20241010
22+
shm_size: 2gb
23+
depends_on:
24+
- selenium-hub
25+
environment:
26+
- SE_EVENT_BUS_HOST=selenium-hub
27+
- SE_EVENT_BUS_PUBLISH_PORT=4442
28+
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
29+
- SE_NODE_MAX_SESSIONS=2
30+
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
31+
ports:
32+
- "6902:5900"
33+
34+
selenium-hub:
35+
image: selenium/hub:4.25.0-20241010
36+
ports:
37+
- "4442:4442"
38+
- "4443:4443"
39+
- "4444:4444"

resources/drivers/geckodriver

5.77 MB
Binary file not shown.

runner.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from pycommons.lang.stringutils import StringUtils
2+
from utils.docker_compose_actions import start_docker_compose, stop_docker_compose
3+
from utils.helper_utils import prepare_dirs, execute_command_using_popen
4+
from utils.reporting.generate_report import generate_allure_report
5+
from helpers.constants.framework_constants import FrameworkConstants as Fc
6+
7+
import logging
8+
9+
def logs():
10+
logger = logging.getLogger()
11+
logger.handlers.clear()
12+
logging.basicConfig(
13+
level=logging.INFO,
14+
format="%(message)s"
15+
)
16+
return logger
17+
18+
19+
20+
def main():
21+
process = None
22+
log = logs()
23+
# Read and print the output line by line
24+
try:
25+
prepare_dirs()
26+
# start_docker_compose(log)
27+
process = execute_command_using_popen(f"behavex {Fc.features} -c {Fc.conf_behavex} --parallel-processes 2 --parallel-delay 1000 --parallel-scheme feature --show-progress-bar")
28+
while True:
29+
output = process.stdout.readline()
30+
if output == StringUtils.EMPTY and process.poll() is not None:
31+
break
32+
if output:
33+
log.info(output.strip())
34+
except KeyboardInterrupt:
35+
log.error("Process terminated by user.")
36+
process.terminate()
37+
finally:
38+
generate_allure_report(log)
39+
# stop_docker_compose(log)
40+
41+
if __name__ == "__main__":
42+
main()

tests/__init__.py

Whitespace-only changes.

tests/environment.py

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import datetime
2+
3+
from behave.runner import Context
4+
from behavex_images import image_attachments
5+
from behavex_images.image_attachments import AttachmentsCondition
6+
from playwright.sync_api import Page
7+
8+
from helpers.constants.framework_constants import FrameworkConstants as Fc
9+
from utils.reporting.logger import get_logs
10+
from utils.reporting.screenshots import attach_screenshot_in_report
11+
from utils.browser_utils import prepare_browser, test_tracing
12+
13+
14+
def before_all(context: Context):
15+
current_time = datetime.datetime.now()
16+
file_name = current_time.strftime("%d_%m_%y-%H_%M_%S_%f")[:-3]
17+
global logger, details
18+
logger = get_logs(f"{Fc.logs_dir}/{file_name}.txt")
19+
# start_docker_compose(logger)
20+
image_attachments.set_attachments_condition(context, AttachmentsCondition.ALWAYS)
21+
22+
23+
def before_feature(context: Context, feature):
24+
logger.info(f"Feature file: {feature.filename}")
25+
logger.info(f"Number of Scenarios: {len(feature.scenarios)}")
26+
formatted_tags = " ".join([f"@{tag}" for tag in feature.tags])
27+
if formatted_tags:
28+
logger.info(f"{formatted_tags}")
29+
logger.info(f"Feature: {feature.name}")
30+
prepare_browser(context)
31+
32+
def before_scenario(context, scenario):
33+
formatted_tags = " ".join([f"@{tag}" for tag in scenario.tags])
34+
if formatted_tags:
35+
logger.info(f"{formatted_tags}")
36+
logger.info(f"Scenario: {scenario.name}")
37+
38+
if scenario.feature.background:
39+
background = scenario.feature.background
40+
for step in background.steps:
41+
logger.info(f"{step.keyword} {step.name}")
42+
43+
for step in scenario.steps:
44+
logger.info(f"{step.keyword} {step.name}")
45+
46+
47+
def after_step(context: Context, step):
48+
current_time = datetime.datetime.now()
49+
file_name = current_time.strftime("%d_%m_%y-%H_%M_%S_%f")[:-3]
50+
page: Page = context.page
51+
page.wait_for_load_state()
52+
page.screenshot(path=f"{Fc.screenshots_dir}/{file_name}.png")
53+
attach_screenshot_in_report(f"{Fc.screenshots_dir}/{file_name}.png")
54+
image_attachments.attach_image_file(context, f"{Fc.screenshots_dir}/{file_name}.png")
55+
56+
def after_scenario(context, scenario):
57+
logger.info(f"Scenario status: {scenario.status}")
58+
59+
60+
def after_feature(context, feature):
61+
test_tracing(context, False)
62+
try:
63+
logger.info(f"Feature Status: {feature.status}")
64+
finally:
65+
context.page.close()
66+
context.browser.close()
67+
context.playwright.stop()
68+
69+
# def after_all(context):
70+
# try:
71+
# generate_allure_report(logger)
72+
# finally:
73+
# stop_docker_compose(logger)

tests/features/homepage.feature

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@Homepage
2+
Feature: Homepage
3+
4+
Background:
5+
Given User navigates to login page
6+
7+
Scenario: Verify if user is able to see 'Swag Labs' on login
8+
When User enters username as 'problem_user'
9+
And User enters password for 'problem_user'
10+
And User clicks on 'Login' button
11+
Then Homepage is displayed
12+
13+
Scenario: Verify if user is able to see 'Swag Labs' on login
14+
When User enters username as 'problem_user'
15+
And User enters password for 'problem_user'
16+
And User clicks on 'Login' button
17+
Then Homepage is displayed
18+
19+
Scenario: This is failed test case
20+
When User enters username as 'problem_user'
21+
And User enters password for 'problem_user'
22+
And User clicks on 'Login' button
23+
Then Product is displayed

tests/features/login.feature

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@Login
2+
Feature: Login
3+
4+
Scenario: Verify if user is able to login with valid user
5+
Given User navigates to login page
6+
When User enters username as 'standard_user'
7+
And User enters password for 'standard_user'
8+
And User clicks on 'Login' button
9+
Then Homepage is displayed
10+
And Labels are present
11+
| label_name |
12+
| Products |
13+
14+
Scenario Outline: Verify if user is able to login with '<username>'
15+
Given User navigates to login page
16+
When User enters username as '<username>'
17+
And User enters password for '<username>'
18+
And User clicks on 'Login' button
19+
Then Homepage is displayed
20+
Examples:
21+
| username |
22+
| performance_glitch_user |
23+
| error_user |
24+
| locked_out_user |

tests/pages/__init__.py

Whitespace-only changes.

tests/pages/base_page.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
class BasePage:
3+
def __init__(self, page):
4+
self.page = page
5+
6+
def open_page(self, url):
7+
self.page.goto(url)
8+
9+
def send_keys(self, locator, text):
10+
self.page.locator(locator).fill(text)
11+
12+
def click(self, locator):
13+
self.page.locator(locator).click()
14+
15+
def get_title(self):
16+
return self.page.title().strip()
17+
18+
def get_text(self, locator):
19+
return self.page.locator(locator).text_content().strip()

tests/pages/homepage_page.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from tests.pages.base_page import BasePage
2+
3+
4+
class Homepage:
5+
6+
products_label = "//span[normalize-space()='Products']"
7+
8+
def __init__(self, page):
9+
self.bp = BasePage(page)
10+
11+
def get_text_from_page(self):
12+
self.bp.get_text(self.products_label)

0 commit comments

Comments
 (0)