dashboard_test.py 4.9 KB

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