From c8c7b52c8c1890f49a3101d02ae119e635726eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Helebrant?= <jiri.helebrant@nic.cz> Date: Mon, 8 Aug 2016 18:08:24 +0200 Subject: [PATCH] Rewrite tests to Nightwatch + Selenium. Improvements over old python-selenium interface: - actually works with up-to-date browsers - works with chromium without installing shady extensions - code is much cleaner and shorter (like 4 times shorter), tests run faster --- run_tests.sh | 29 ++++++++++++---- tests/.gitignore | 1 + tests/DevicesTableLoads.py | 21 ------------ tests/Help.js | 11 ++++++ tests/HelpIndexBuilds.py | 21 ------------ tests/Map.js | 10 ++++++ tests/MapLayersLoad.py | 22 ------------ tests/Monkey.py | 63 ----------------------------------- tests/OperatorsTableLoads.py | 21 ------------ tests/PersonalHistoryLoads.py | 37 -------------------- tests/ProjectBar.js | 10 ++++++ tests/ProjectsBarLoads.py | 21 ------------ tests/ResultDetail.js | 13 ++++++++ tests/SpeedChartDraws.py | 53 ----------------------------- tests/Stats.js | 26 +++++++++++++++ tests/StatsTableLoads.py | 21 ------------ tests/nightwatch.json | 46 +++++++++++++++++++++++++ 17 files changed, 139 insertions(+), 287 deletions(-) create mode 100644 tests/.gitignore delete mode 100644 tests/DevicesTableLoads.py create mode 100644 tests/Help.js delete mode 100644 tests/HelpIndexBuilds.py create mode 100644 tests/Map.js delete mode 100644 tests/MapLayersLoad.py delete mode 100644 tests/Monkey.py delete mode 100644 tests/OperatorsTableLoads.py delete mode 100644 tests/PersonalHistoryLoads.py create mode 100644 tests/ProjectBar.js delete mode 100644 tests/ProjectsBarLoads.py create mode 100644 tests/ResultDetail.js delete mode 100644 tests/SpeedChartDraws.py create mode 100644 tests/Stats.js delete mode 100644 tests/StatsTableLoads.py create mode 100644 tests/nightwatch.json diff --git a/run_tests.sh b/run_tests.sh index 58628aa9..6bc44b4b 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,16 +1,31 @@ #!/usr/bin/env bash -cd output && python -m http.server 9000 & - if curl -s http://localhost:4444 | grep Jetty > /dev/null; then echo "Using Selenium at localhost:4444." else echo "Selenium not running."; + echo "Please start Selenium server at localhost:4444 (java -jar selenium-server-standalone-X.X.X.jar)"; + exit 1; +fi + +NIGHTWATCH_BIN=$(which nightwatch) +if [[ -x "$NIGHTWATCH_BIN" ]]; then + echo "Using Nightwatch from $NIGHTWATCH_BIN" +else + echo "Nightwatch not found in your PATH. Install guide can be found at http://nightwatchjs.org/guide#install-nightwatch."; exit 1; fi -for i in tests/*py; do - echo "\n\n"; - echo "Running ${i}:"; - python2 ${i}; -done +PWD=$(pwd) + +mkdir /tmp/netmetr-test +cp -r output /tmp/netmetr-test/netmetr-proto +cd /tmp/netmetr-test/ && python -m http.server 9000 > /dev/null 2>&1 & + +cd $PWD + +cd tests +nightwatch +nightwatch --env chrome + +pkill -f "python.*http.server 9000" diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..27a3afbb --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +reports diff --git a/tests/DevicesTableLoads.py b/tests/DevicesTableLoads.py deleted file mode 100644 index 73c5f203..00000000 --- a/tests/DevicesTableLoads.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import webdriver -import unittest -from selenium.webdriver.firefox.firefox_binary import FirefoxBinary - - -class DevicesTableLoads(unittest.TestCase): - def setUp(self): - binary = FirefoxBinary("/usr/bin/firefox-bin") - self.driver = webdriver.Firefox(firefox_binary=binary) - self.driver.implicitly_wait(10) - - def test_devices_table_loads(self): - self.driver.get("http://localhost:9000/cs/statistiky.html") - self.assertTrue(self.driver.find_element_by_css_selector("#devices td.sorting_1")) - - def tearDown(self): - self.driver.close() - -if __name__ == "__main__": - unittest.main() diff --git a/tests/Help.js b/tests/Help.js new file mode 100644 index 00000000..d4422611 --- /dev/null +++ b/tests/Help.js @@ -0,0 +1,11 @@ +module.exports = { + "Help index builds" : function (browser) { + browser + .url(browser.launchUrl + "napoveda.html") + .waitForElementVisible("body") + .click("div.toc ul:first-child li:first-child") + .pause(1000) + .waitForElementVisible("div.toc ul:first-child li:first-child li:first-child a span") + .end(); + } +}; diff --git a/tests/HelpIndexBuilds.py b/tests/HelpIndexBuilds.py deleted file mode 100644 index e9b02926..00000000 --- a/tests/HelpIndexBuilds.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import webdriver -import unittest -from selenium.webdriver.firefox.firefox_binary import FirefoxBinary - - -class HelpIndexBuilds(unittest.TestCase): - def setUp(self): - binary = FirefoxBinary("/usr/bin/firefox-bin") - self.driver = webdriver.Firefox(firefox_binary=binary) - self.driver.implicitly_wait(10) - - def test_help_index_builds(self): - self.driver.get("http://localhost:9000/cs/napoveda.html") - self.assertTrue(self.driver.find_element_by_css_selector("div.toc ul li a span")) - - def tearDown(self): - self.driver.close() - -if __name__ == "__main__": - unittest.main() diff --git a/tests/Map.js b/tests/Map.js new file mode 100644 index 00000000..fe63b0e0 --- /dev/null +++ b/tests/Map.js @@ -0,0 +1,10 @@ +module.exports = { + "Heatmap layer loads" : function (browser) { + browser + .url(browser.launchUrl + "mapa.html") + .waitForElementVisible("body") + .waitForElementNotVisible(".loader-overlay") + .waitForElementVisible(".leaflet-layer[style~='z-index: 3'] .leaflet-tile-loaded:first-child") + .end(); + } +}; diff --git a/tests/MapLayersLoad.py b/tests/MapLayersLoad.py deleted file mode 100644 index 62bb121a..00000000 --- a/tests/MapLayersLoad.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import webdriver -import unittest -from selenium.webdriver.firefox.firefox_binary import FirefoxBinary - - -class MapLayersLoad(unittest.TestCase): - def setUp(self): - binary = FirefoxBinary("/usr/bin/firefox-bin") - self.driver = webdriver.Firefox(firefox_binary=binary) - self.driver.implicitly_wait(10) - - def test_map_layers_load(self): - self.driver.get("http://localhost:9000/cs/mapa.html") - self.assertTrue("img.leaflet-tile-loaded") - self.assertTrue(self.driver.find_element_by_css_selector("div.leaflet-control-layers-overlays > label > span")) - - def tearDown(self): - self.driver.close() - -if __name__ == "__main__": - unittest.main() diff --git a/tests/Monkey.py b/tests/Monkey.py deleted file mode 100644 index d6a9f44a..00000000 --- a/tests/Monkey.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import selenium, webdriver -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer -from urlparse import parse_qs, urlparse -from os import system -import time -import thread - -base_url = "http://localhost:9000/netmetr/" -pages = [ - "index.html", - "mapa.html", - "aplikace.html", - "moje.html", - "statistiky.html", - "open-data.html", - "napoveda.html", - ] - - -class httpHandler(BaseHTTPRequestHandler): - def do_GET(self): - self.send_response(200) - self.send_header('Content-type', 'application/json') - self.send_header('Access-Control-Allow-Origin', '*') - self.end_headers() - q = parse_qs(urlparse(self.path).query) - print " " + q['msg'][0] - return - - def log_message(self, format, *args): - return - - -def release_monkeys(page): - print page - driver.get(base_url + page) - driver.execute_script("$.getScript('//cdnjs.cloudflare.com/ajax/libs/gremlins.js/0.1.0/" + - "gremlins.min.js').done(function(){gremlins.createHorde().unleash();})") - time.sleep(11) - - -def run_error_logger(port): - try: - server = HTTPServer(('', port), httpHandler) - server.serve_forever() - except KeyboardInterrupt: - server.socket.close() - -thread.start_new_thread(run_error_logger, (4445,)) - -system("sed -i \"s~<head>~<head><script>var req=new XMLHttpRequest;(function(){console.error=" + - "function(m){req.open('GET','http://localhost:4445?msg='+m);req.send()}})();" + - "window.onerror=function (m,u,l){req.open('GET','http://localhost:4445?msg='+" + - "u.replace(/.*theme/,'')+':'+l+': '+m);req.send()};</script>~\" netmetr/*.html") - -driver = webdriver.Chrome() - -for page in pages: - release_monkeys(page) - -driver.quit() -system("sed -i \"s~<head>.*charset~<head><meta charset~\" netmetr/*.html") diff --git a/tests/OperatorsTableLoads.py b/tests/OperatorsTableLoads.py deleted file mode 100644 index 3ba34855..00000000 --- a/tests/OperatorsTableLoads.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import webdriver -import unittest -from selenium.webdriver.firefox.firefox_binary import FirefoxBinary - - -class DevicesTableLoads(unittest.TestCase): - def setUp(self): - binary = FirefoxBinary("/usr/bin/firefox-bin") - self.driver = webdriver.Firefox(firefox_binary=binary) - self.driver.implicitly_wait(10) - - def test_devices_table_loads(self): - self.driver.get("http://localhost:9000/cs/statistiky.html") - self.assertTrue(self.driver.find_element_by_css_selector("#operators td.sorting_1")) - - def tearDown(self): - self.driver.close() - -if __name__ == "__main__": - unittest.main() diff --git a/tests/PersonalHistoryLoads.py b/tests/PersonalHistoryLoads.py deleted file mode 100644 index 798c69fd..00000000 --- a/tests/PersonalHistoryLoads.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import selenium -import unittest -import time -import re - - -class OperatorsTableLoads(unittest.TestCase): - def setUp(self): - self.verificationErrors = [] - self.selenium = selenium("localhost", 4444, "*chrome", "http://localhost:9000/") - self.selenium.start() - - def test_history_table_loads(self): - sel = self.selenium - sel.open("/netmetr/moje.html") - sel.wait_for_page_to_load("30000") - time.sleep(1) - sel.type("id=sync-code", "9a12c5b843be") - time.sleep(1) - sel.click("//button[@id='send-sync-code']") - for i in range(10): - try: - if sel.is_element_present("css=#stats td.sorting_1"): - break - except: - pass - time.sleep(1) - else: - self.fail("time out") - - def tearDown(self): - self.selenium.stop() - self.assertEqual([], self.verificationErrors) - -if __name__ == "__main__": - unittest.main() diff --git a/tests/ProjectBar.js b/tests/ProjectBar.js new file mode 100644 index 00000000..d1e7f332 --- /dev/null +++ b/tests/ProjectBar.js @@ -0,0 +1,10 @@ +module.exports = { + "Project bar loads" : function (browser) { + browser + .url(browser.launchUrl + "napoveda.html") + .resizeWindow(1000, 800) + .waitForElementVisible("body") + .waitForElementVisible("#tb-projects-bar") + .end(); + } +}; diff --git a/tests/ProjectsBarLoads.py b/tests/ProjectsBarLoads.py deleted file mode 100644 index 0d386a0c..00000000 --- a/tests/ProjectsBarLoads.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import webdriver -import unittest -from selenium.webdriver.firefox.firefox_binary import FirefoxBinary - - -class DevicesTableLoads(unittest.TestCase): - def setUp(self): - binary = FirefoxBinary("/usr/bin/firefox-bin") - self.driver = webdriver.Firefox(firefox_binary=binary) - self.driver.implicitly_wait(10) - - def test_devices_table_loads(self): - self.driver.get("http://localhost:9000/cs/statistiky.html") - self.assertTrue(self.driver.find_element_by_css_selector("#tb-projects-bar")) - - def tearDown(self): - self.driver.close() - -if __name__ == "__main__": - unittest.main() diff --git a/tests/ResultDetail.js b/tests/ResultDetail.js new file mode 100644 index 00000000..3a2bbaba --- /dev/null +++ b/tests/ResultDetail.js @@ -0,0 +1,13 @@ +module.exports = { + "Speed chart draws" : function (browser) { + browser + .url(browser.launchUrl + "statistiky.html") + .waitForElementVisible("body") + .waitForElementNotVisible(".loader-overlay") + .click("#results tr:first-child td.datetime a") + .waitForElementVisible("body") + .waitForElementNotVisible(".loader-overlay") + .waitForElementVisible("#placeholder-dl canvas.flot-overlay") + .end(); + } +}; diff --git a/tests/SpeedChartDraws.py b/tests/SpeedChartDraws.py deleted file mode 100644 index b68ad4f3..00000000 --- a/tests/SpeedChartDraws.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import selenium -import unittest -import time -import re - - -class SpeedChartsDraw(unittest.TestCase): - def setUp(self): - self.verificationErrors = [] - self.selenium = selenium("localhost", 4444, "*chrome", "http://localhost:9000/") - self.selenium.start() - - def test_speed_charts_draw(self): - sel = self.selenium - - sel.open("/netmetr/statistiky.html") - sel.wait_for_page_to_load("30000") - sel.run_script("window.scrollTo(0, document.body.scrollHeight);") - - for i in range(10): - try: - if sel.is_element_present("css=table#stats td.datetime a"): - break - except: - pass - time.sleep(1) - else: - self.fail("Timed out when loading results table.") - - sel.click("css=table#stats td.datetime a") - - for i in range(10): - try: - if sel.is_element_present("css=table#detail-speed"): - break - except: - pass - time.sleep(1) - else: - self.fail("Timed out when loading result detail.") - - try: - self.failUnless(sel.is_element_present("css=canvas.flot-overlay")) - except AssertionError, e: - self.verificationErrors.append(str(e)) - - def tearDown(self): - self.selenium.stop() - self.assertEqual([], self.verificationErrors) - -if __name__ == "__main__": - unittest.main() diff --git a/tests/Stats.js b/tests/Stats.js new file mode 100644 index 00000000..e998898b --- /dev/null +++ b/tests/Stats.js @@ -0,0 +1,26 @@ +module.exports = { + "Devices table loads" : function (browser) { + browser + .url(browser.launchUrl + "statistiky.html") + .waitForElementVisible("body") + .waitForElementNotVisible(".loader-overlay") + .waitForElementVisible("#devices tr:first-child td.sorting_1") + .end(); + }, + "Operators table loads" : function (browser) { + browser + .url(browser.launchUrl + "statistiky.html") + .waitForElementVisible("body") + .waitForElementNotVisible(".loader-overlay") + .waitForElementVisible("#operators tr:first-child td.sorting_1") + .end(); + }, + "Stats table loads" : function (browser) { + browser + .url(browser.launchUrl + "statistiky.html") + .waitForElementVisible("body") + .waitForElementNotVisible(".loader-overlay") + .waitForElementVisible("#operators tr:first-child td.sorting_1") + .end(); + } +}; diff --git a/tests/StatsTableLoads.py b/tests/StatsTableLoads.py deleted file mode 100644 index 41011a30..00000000 --- a/tests/StatsTableLoads.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -from selenium import webdriver -import unittest -from selenium.webdriver.firefox.firefox_binary import FirefoxBinary - - -class DevicesTableLoads(unittest.TestCase): - def setUp(self): - binary = FirefoxBinary("/usr/bin/firefox-bin") - self.driver = webdriver.Firefox(firefox_binary=binary) - self.driver.implicitly_wait(10) - - def test_devices_table_loads(self): - self.driver.get("http://localhost:9000/cs/statistiky.html") - self.assertTrue(self.driver.find_element_by_css_selector("#stats td.sorting_1")) - - def tearDown(self): - self.driver.close() - -if __name__ == "__main__": - unittest.main() diff --git a/tests/nightwatch.json b/tests/nightwatch.json new file mode 100644 index 00000000..6ec046d3 --- /dev/null +++ b/tests/nightwatch.json @@ -0,0 +1,46 @@ +{ + "src_folders": ["."], + "output_folder": "reports", + "custom_commands_path": "", + "custom_assertions_path": "", + "page_objects_path": "", + "globals_path": "", + + "selenium": { + "start_process": false, + "server_path": "", + "log_path": "", + "host": "127.0.0.1", + "port": 4444, + "cli_args": { + "webdriver.chrome.driver": "" + } + }, + + "test_settings": { + "default": { + "launch_url": "http://localhost:9000/netmetr-proto/cs/", + "selenium_port": 4444, + "selenium_host": "localhost", + "silent": true, + "screenshots": { + "enabled": false, + "path": "" + }, + "desiredCapabilities": { + "browserName": "firefox", + "javascriptEnabled": true + }, + "globals": { + "waitForConditionTimeout": 3000 + } + }, + + "chrome": { + "desiredCapabilities": { + "browserName": "chrome", + "javascriptEnabled": true + } + } + } +} -- GitLab