Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development selenium add stats page tests 1 #3313

Merged
merged 16 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/playground/tests/frontend_selenium/Config.ini
Marinaa-Emad marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ net = dev
seed =
A-Harby marked this conversation as resolved.
Show resolved Hide resolved
node_seed =
address =
email =
email =
89 changes: 89 additions & 0 deletions packages/playground/tests/frontend_selenium/pages/statistics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException



class StatisticsPage:

logout_button = (By.XPATH, "//button[.//span[text()=' Logout ']]")
tfgrid_button = (By.XPATH, "//span[text()='TFGrid']")
grid_status_button = (By.XPATH, "//span[text()='Grid Status']")
node_monitoring_button = (By.XPATH, "//span[text()='Node Monitoring']")
statistics_button = (By.XPATH, "//span[text()='Node Statistics']")
statistics_label = (By.XPATH, "//*[contains(text(), 'Statistics')]")
map = (By.XPATH,"//button[contains(@class, 'btn-main-container')]")
nodes_online = (By.XPATH, "//span[text()='Nodes Online']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
dedicated_machines = (By.XPATH, "//span[text()='Dedicated Machines']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
farms = (By.XPATH, "//span[text()='Farms']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
countries = (By.XPATH, "//span[text()='Countries']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
cpus = (By.XPATH, "//span[text()='CPUs']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
ssd_storage = (By.XPATH, "//span[text()='SSD Storage']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
hdd_storage = (By.XPATH, "//span[text()='HDD Storage']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
ram = (By.XPATH, "//span[text()='RAM']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
gpus = (By.XPATH, "//span[text()='GPUs']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
access_nodes = (By.XPATH, "//span[text()='Access Nodes']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
gateways = (By.XPATH, "//span[text()='Gateways']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
twins = (By.XPATH, "//span[text()='Twins']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
public_ips = (By.XPATH, "//span[text()='Public IPs']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
conracts = (By.XPATH, "//span[text()='Contracts']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")
number_of_workloads = (By.XPATH, "//span[text()='Number of workloads']/ancestor::div/following-sibling::div[@class='v-card-text card-body']")


def __init__(self, browser):
self.browser = browser

def navigate(self):
webdriver.ActionChains(self.browser).send_keys(Keys.ESCAPE).perform()
self.browser.find_element(*self.tfgrid_button).click()
self.browser.find_element(*self.statistics_button).click()
WebDriverWait(self.browser, 60).until(EC.visibility_of_element_located(self.statistics_label))

def statistics_detials(self):
details = {}
wait = WebDriverWait(self.browser, 60) # Increased wait time to 60 seconds
elements_to_fetch = {
"nodes": self.nodes_online,
"dedicatedNodes": self.dedicated_machines,
"farms": self.farms,
"countries": self.countries,
"totalCru": self.cpus,
"totalSru": self.ssd_storage,
"totalHru": self.hdd_storage,
"totalMru": self.ram,
"gpus": self.gpus,
"accessNodes": self.access_nodes,
"gateways": self.gateways,
"twins": self.twins,
"publicIps": self.public_ips,
"contracts": self.conracts,
"workloads_number": self.number_of_workloads
}

for key, locator in elements_to_fetch.items():
try:
element_text = wait.until(EC.visibility_of_element_located(locator)).text
details[key] = element_text
except TimeoutException:
details[key] = None # Add None or some default value to maintain dictionary consistency

return details

def get_link(self):
WebDriverWait(self.browser, 30).until(EC.number_of_windows_to_be(2))
self.browser.switch_to.window(self.browser.window_handles[1])
url = self.browser.current_url
self.browser.close()
self.browser.switch_to.window(self.browser.window_handles[0])
return url

def grid_status_link(self):
self.browser.find_element(*self.grid_status_button).click()
return self.get_link()

def node_monitoring_link(self):
self.browser.find_element(*self.node_monitoring_button).click()
return self.get_link()
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import math
from utils.utils import byte_converter,convert_to_scaled_float
from pages.statistics import StatisticsPage
from utils.grid_proxy import GridProxy
from pages.dashboard import DashboardPage

def before_test_setup(browser):
statistics_page = StatisticsPage(browser)
dashboard_page = DashboardPage(browser)
dashboard_page.open_and_load()
statistics_page.navigate()
return statistics_page


def test_statistics_details(browser):
"""
TC1503 - Verify Statistics
Steps:
- Navigate to the dashboard.
- Click on TFGrid from side menu.
- Click on Stats.
Result: Assert that the displayed values should match the data from the grid proxy.
"""
statistics_page = before_test_setup(browser)
grid_proxy = GridProxy(browser)
statistics_details = statistics_page.statistics_detials()
grid_statistics_details = grid_proxy.get_stats()
# Convert necessary values from string to integer for comparison, but keeping the dictionary structure
statistics_details_converted = {
key: int(value.replace(',', '')) if value is not None and value.replace(',', '').isdigit() else value
for key, value in statistics_details.items()
}
# Full set of assertions, comparing UI stats with proxy stats
assert grid_statistics_details['nodes'] == statistics_details_converted['nodes']
assert grid_statistics_details['dedicatedNodes'] == statistics_details_converted['dedicatedNodes']
assert grid_statistics_details['farms'] == statistics_details_converted['farms']
assert grid_statistics_details['countries'] == statistics_details_converted['countries']
assert grid_statistics_details['totalCru'] == statistics_details_converted['totalCru']
assert math.isclose(convert_to_scaled_float(grid_statistics_details['totalSru']), convert_to_scaled_float(byte_converter(statistics_details_converted['totalSru'])), abs_tol=0.002)
assert math.isclose(convert_to_scaled_float(grid_statistics_details['totalHru']), convert_to_scaled_float(byte_converter(statistics_details_converted['totalHru'])), abs_tol=0.002)
assert math.isclose(convert_to_scaled_float(grid_statistics_details['totalMru']), convert_to_scaled_float(byte_converter(statistics_details_converted['totalMru'])), abs_tol=0.002)
assert grid_statistics_details['gpus'] == statistics_details_converted['gpus']
assert grid_statistics_details['accessNodes'] == statistics_details_converted['accessNodes']
assert grid_statistics_details['gateways'] == statistics_details_converted['gateways']
assert grid_statistics_details['twins'] == statistics_details_converted['twins']
assert grid_statistics_details['publicIps'] == statistics_details_converted['publicIps']
assert grid_statistics_details['contracts'] == statistics_details_converted['contracts']
assert grid_statistics_details['workloads_number'] == statistics_details_converted['workloads_number']


def test_tfgrid_links(browser):
A-Harby marked this conversation as resolved.
Show resolved Hide resolved
"""
TC2867 - Verify TFGrid links
Steps:
- Navigate to the dashboard.
- Click on TFGrid from side menu.
- Click on Grid Status.
- Click on Node Monitoring.
Result: Assert that The links match the pages.
"""
statistics_page = before_test_setup(browser)
assert statistics_page.grid_status_link() == 'https://status.grid.tf/status/threefold/'
assert statistics_page.node_monitoring_link() == 'https://metrics.grid.tf/d/rYdddlPWkfqwf/zos-host-metrics?orgId=2&refresh=30s/'
35 changes: 32 additions & 3 deletions packages/playground/tests/frontend_selenium/utils/grid_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,36 @@ def get_twin_node(self, twin_id):
details = r.json()
return details


def get_stats(self):
r = requests.post('https://stats.' + Base.net + '.grid.tf/api/stats-summary')
stats_json = r.json()
return list(stats_json.values())
up = requests.get(Base.gridproxy_url + 'stats?status=up', timeout=10).json()
standby = requests.get(Base.gridproxy_url + 'stats?status=standby', timeout=10).json()
# Initialize a dictionary to store the merged data
merged_data = {}
# Merge simple values, summing if they differ
keys_to_sum = ['nodes', 'accessNodes', 'totalCru', 'totalSru', 'totalMru', 'totalHru', 'gpus', 'dedicatedNodes', 'workloads_number']
for key in keys_to_sum:
merged_data[key] = up[key] + standby[key]
# Merge the "farms", "publicIps", "gateways", "twins", and "contracts" fields (they are the same)
keys_to_add_once = ['farms', 'publicIps', 'gateways', 'twins', 'contracts']
for key in keys_to_add_once:
merged_data[key] = up[key]
# Merge nodesDistribution and calculate unique and common countries
up_distribution = up['nodesDistribution']
standby_distribution = standby['nodesDistribution']
merged_distribution = {}
common_countries = 0
for country, up_count in up_distribution.items():
standby_count = standby_distribution.get(country, 0)
merged_distribution[country] = up_count + standby_count
if standby_count > 0:
common_countries += 1
for country, standby_count in standby_distribution.items():
if country not in merged_distribution:
merged_distribution[country] = standby_count
merged_data['nodesDistribution'] = merged_distribution
# Calculate the total countries: all unique countries minus common countries
total_countries = len(merged_distribution) # Total unique countries
merged_data['countries'] = total_countries
# Return the dictionary directly
return merged_data
34 changes: 26 additions & 8 deletions packages/playground/tests/frontend_selenium/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,16 +176,34 @@ def randomize_public_ipv4():
ip_subnet = ip + '/' + random.choice(['26', '27', '28', '29'])
return ip_subnet, ip


def convert_to_scaled_float(number):
str_number = str(number)
if '.' in str_number:
decimal_index = str_number.index('.')
else:
decimal_index = len(str_number)
divisor = 10 ** decimal_index
scaled_number = number / divisor
return scaled_number


def byte_converter(value):
A-Harby marked this conversation as resolved.
Show resolved Hide resolved
# Define the unit and the numeric value before checking conditions
unit = value[-2].upper() # Last character represents the unit (P, T, G)
number_str = value[:-3].strip() # Everything except the last two characters is the number

if value != '0':
if value[-2] == 'P':
return float(value[:-3])*(1024*2)
elif value[-2] == 'T':
return float(value[:-3])*1024
else:
return float(value[:-3])
else:
return float(value)
# Convert based on the unit
if unit == 'P': # Petabytes
return float(number_str) * (1024 ** 5)
elif unit == 'T': # Terabytes
return float(number_str) * (1024 ** 4)
elif unit == 'G': # Gigabytes
return float(number_str) * (1024 ** 3)

return float(value)


def get_min(nodes, resource):
min = nodes[0][resource]
Expand Down
Loading