dashboard_test.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import os
  2. import time
  3. import unittest
  4. import pytest
  5. import requests
  6. import logging
  7. from urllib.parse import urljoin
  8. from selenium import webdriver
  9. from selenium.webdriver.common.by import By
  10. from selenium.webdriver.common.keys import Keys
  11. from selenium.webdriver.chrome.options import Options
  12. from selenium.webdriver.support.wait import WebDriverWait
  13. from selenium.webdriver.common import utils
  14. from selenium.common.exceptions import NoSuchElementException
  15. logger = logging.getLogger()
  16. logger.setLevel(logging.INFO)
  17. @pytest.fixture
  18. def driver():
  19. options = Options()
  20. options.add_argument("--headless")
  21. options.add_argument("--no-sandbox")
  22. _driver = webdriver.Chrome(options=options)
  23. yield _driver
  24. _driver.quit()
  25. @pytest.fixture(autouse=True)
  26. def dashboard_url(dashboard_host, dashboard_port):
  27. count = 0
  28. while utils.is_connectable(port=dashboard_port, host=dashboard_host) is False:
  29. if count == 30:
  30. raise Exception("Dashboard is not ready")
  31. count += 1
  32. time.sleep(1)
  33. return f"http://{dashboard_host}:{dashboard_port}"
  34. def login(driver, dashboard_url):
  35. # admin is set in CI jobs, hence as default value
  36. password = os.getenv("EMQX_DASHBOARD__DEFAULT_PASSWORD", "admin")
  37. driver.get(dashboard_url)
  38. assert "EMQX Dashboard" == driver.title
  39. assert f"{dashboard_url}/#/login?to=/dashboard/overview" == driver.current_url
  40. driver.execute_script("window.localStorage.setItem('licenseTipVisible','false');")
  41. driver.find_element(By.XPATH, "//div[@class='login']//form//input[@type='text']").send_keys("admin")
  42. driver.find_element(By.XPATH, "//div[@class='login']//form//input[@type='password']").send_keys(password)
  43. driver.find_element(By.XPATH, "//div[@class='login']//form//button").click()
  44. dest_url = urljoin(dashboard_url, "/#/dashboard/overview")
  45. ensure_current_url(driver, dest_url)
  46. assert len(driver.find_elements(By.XPATH, "//div[@class='login']")) == 0
  47. logger.info(f"Logged in to {dashboard_url}")
  48. def ensure_current_url(d, url):
  49. count = 0
  50. while url != d.current_url:
  51. if count == 10:
  52. raise Exception(f"Failed to load {url}")
  53. count += 1
  54. time.sleep(1)
  55. def title(d):
  56. title = ''
  57. for _ in range(5):
  58. try:
  59. title = d.find_element("xpath", "//div[@id='app']//h1[@class='header-title']")
  60. break
  61. except NoSuchElementException:
  62. time.sleep(1)
  63. else:
  64. raise AssertionError("Cannot find the title element")
  65. return title
  66. def wait_title_text(d, text):
  67. return WebDriverWait(d, 10).until(lambda x: title(x).text == text)
  68. def test_basic(driver, dashboard_url):
  69. login(driver, dashboard_url)
  70. logger.info(f"Current URL: {driver.current_url}")
  71. wait_title_text(driver, "Cluster Overview")
  72. def test_log(driver, dashboard_url):
  73. login(driver, dashboard_url)
  74. logger.info(f"Current URL: {driver.current_url}")
  75. dest_url = urljoin(dashboard_url, "/#/log")
  76. driver.get(dest_url)
  77. ensure_current_url(driver, dest_url)
  78. wait_title_text(driver, "Logging")
  79. label = driver.find_element(By.XPATH, "//div[@id='app']//form//label[contains(., 'Enable Log Handler')]")
  80. assert driver.find_elements(By.ID, label.get_attribute("for"))
  81. label = driver.find_element(By.XPATH, "//div[@id='app']//form//label[contains(., 'Log Level')]")
  82. assert driver.find_elements(By.ID, label.get_attribute("for"))
  83. label = driver.find_element(By.XPATH, "//div[@id='app']//form//label[contains(., 'Log Formatter')]")
  84. assert driver.find_elements(By.ID, label.get_attribute("for"))
  85. label = driver.find_element(By.XPATH, "//div[@id='app']//form//label[contains(., 'Time Offset')]")
  86. assert driver.find_elements(By.ID, label.get_attribute("for"))
  87. def fetch_version_info(dashboard_url):
  88. status_url = urljoin(dashboard_url, "/status?format=json")
  89. response = requests.get(status_url)
  90. response.raise_for_status()
  91. return response.json()
  92. def parse_version(version_str):
  93. prefix_major, minor, _ = version_str.split('.', 2)
  94. prefix = prefix_major[:1]
  95. major = prefix_major[1:]
  96. return prefix, major + '.' + minor
  97. def fetch_version(url):
  98. info = fetch_version_info(url)
  99. version_str = info['rel_vsn']
  100. return parse_version(version_str)
  101. def test_docs_link(driver, dashboard_url):
  102. login(driver, dashboard_url)
  103. logger.info(f"Current URL: {driver.current_url}")
  104. xpath_link_help = "//div[@id='app']//div[@class='nav-header']//a[contains(@class, 'link-help')]"
  105. # retry up to 5 times
  106. for _ in range(5):
  107. try:
  108. link_help = driver.find_element(By.XPATH, xpath_link_help)
  109. break
  110. except NoSuchElementException:
  111. time.sleep(1)
  112. else:
  113. raise AssertionError("Cannot find the help link")
  114. driver.execute_script("arguments[0].click();", link_help)
  115. prefix, emqx_version = fetch_version(dashboard_url)
  116. # it's v5.x in the url
  117. emqx_version = 'v' + emqx_version
  118. docs_base_url = "https://docs.emqx.com/en/emqx"
  119. docs_url = f"{docs_base_url}/{emqx_version}"
  120. xpath = f"//div[@id='app']//div[@class='nav-header']//a[@href[starts-with(.,'{docs_url}')]]"
  121. try:
  122. driver.find_element(By.XPATH, xpath)
  123. except NoSuchElementException:
  124. raise AssertionError(f"Cannot find the doc URL for version {emqx_version}, please make sure the dashboard package is up to date.")