Skip to content

Commit

Permalink
Draft: replace nosetests with pytest
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredkhan committed Jan 18, 2025
1 parent ec648db commit a01594f
Show file tree
Hide file tree
Showing 27 changed files with 182 additions and 219 deletions.
2 changes: 1 addition & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ module.exports = function (grunt) {
}
},
test_server_functional: {
command: 'nosetests3 tests/functional/'
command: 'pytest tests/functional/'
},
test_client: {
command: 'npm run test'
Expand Down
2 changes: 1 addition & 1 deletion controllers/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -1807,7 +1807,7 @@ def licence():
return dict()

def news():
redirect(URL('default', 'milestones.html'))
redirect(URL('default', 'timeline.html'))
return dict()


Expand Down
2 changes: 1 addition & 1 deletion tests/benchmarking/API/test_textsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class TestTextsearch(object):
replicates = 3

@classmethod
def setUpClass(self):
def setup_class(self):
self.web2py = Web2py_server()
wait_for_server_active()
colorama.init()
Expand Down
8 changes: 4 additions & 4 deletions tests/benchmarking/test_viewer_loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
"""
"""

from ...util import base_url
from ..functional_tests import FunctionalTest
from ..util import base_url
from ..functional.functional_tests import FunctionalTest
import os.path

class TestViewerLoading(FunctionalTest):
"""
Test whether the viewer loading functions work
"""
@classmethod
def setUpClass(self):
def setup_class(self):
print("== Running {} ==".format(os.path.basename(__file__)))
super().setUpClass()
super().setup_class()

def test_viewer_loading_time(self):
"""
Expand Down
54 changes: 22 additions & 32 deletions tests/functional/functional_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,26 @@
"""Carry out functional tests on OneZoom pages using an automated browser via selenium
Example: carry out all tests
nosetests -w ./ tests/functional
python -m pytest ./ tests/functional
Example: carry out all tests for unsponsorable sites (museum displays)
nosetests -vs functional/sponsorship/test_unsponsorable_site.py
python -m pytest -s functional/sponsorship/test_unsponsorable_site.py
Example: carry out test that unsponsorable sites give the correct page for invalid otts
nosetests -vs functional/sponsorship/test_unsponsorable_site.py:TestUnsponsorableSite.test_invalid
If you have installed the 'rednose' package (pip3 install rednose), you can get nicer output by e.g.
nosetests -vs ./tests/functional --rednose
python -m pytest -s functional/sponsorship/test_unsponsorable_site.py:TestUnsponsorableSite.test_invalid
To carry out tests on a remote machine, you can specify a [url][server] and [url][port]
in a config file, which will not give the FunctionalTest class an is_local attribute
and hence will skip tests marked @attr('is_local'). E.g. for testing beta.onezoom.org, you can do
TODO: this is not yet implemented for the functional tests
in nosetests we used to do...
nosetests -vs ./tests/functional --rednose --tc-file beta_cfg.ini
"""

import sys
import json
import os
import re
from datetime import datetime
from nose import tools
import requests
import subprocess
import logging
Expand All @@ -52,7 +48,7 @@ class FunctionalTest(object):
is_local = Web2py_server.is_local()

@classmethod
def setUpClass(self):
def setup_class(self):
def striptext_in_file(line, file):
"""
look for the line as a starting line in the file, stripping whitespace
Expand Down Expand Up @@ -97,55 +93,50 @@ def striptext_in_file(line, file):
selenium_logger.setLevel(logging.WARNING)
#chrome_options = webdriver.ChromeOptions()
#chrome_options.add_experimental_option("mobileEmulation", { "deviceName": "iPhone 7" })
self.caps = webdriver.ChromeOptions().to_capabilities()
chrome_options = webdriver.ChromeOptions()
# enable browser logging
self.caps['loggingPrefs'] = { 'browser':'ALL' }
self.browser = webdriver.Chrome(desired_capabilities = self.caps)
chrome_options.set_capability('goog:loggingPrefs', { 'browser':'ALL' })
self.browser = webdriver.Chrome(options=chrome_options)
self.browser.implicitly_wait(1)

@classmethod
def tearDownClass(self):
def teardown_class(self):
#should test here that we don't have any console.log errors (although we might have logs).
self.browser.quit()
self.web2py.stop_server()

def setup(self):
def setup_method(self):
"""
By default, clear logs before each test
"""
self.clear_log()

def teardown(self):
def teardown_method(self):
"""
By default, check javascript errors after each test. If you don't want to do this, e.g. for iframes, thic can be overridden
"""
self.clear_log(check_errors=True)

@tools.nottest
def element_by_tag_name_exists(self, tag_name):
try: self.browser.find_element_by_tag_name(tag_name)
try: self.browser.find_element(By.TAG_NAME, tag_name)
except NoSuchElementException: return False
return True

@tools.nottest
def element_by_id_exists(self, id):
try: self.browser.find_element_by_id(id)
try: self.browser.find_element(By.ID, id)
except NoSuchElementException: return False
return True

@tools.nottest
def element_by_class_exists(self, cls):
try: self.browser.find_element_by_class_name(cls)
try: self.browser.find_element(By.CLASS_NAME, cls)
except NoSuchElementException: return False
return True

@tools.nottest
def element_by_css_selector_exists(self, css):
try: self.browser.find_element_by_css_selector(css)
try: self.browser.find_element(By.CSS_SELECTOR, css)
except NoSuchElementException: return False
return True

@tools.nottest
def clear_log(self, check_errors=False):
log = self.browser.get_log('browser')
if check_errors:
Expand All @@ -155,7 +146,6 @@ def clear_log(self, check_errors=False):
if not (message['message'].startswith("https://media.eol.org/content") and "404 (Not Found)" in message['message']):
assert False, "Javascript issue of level {}, : {}".format(message['level'], message['message'])

@tools.nottest
def zoom_disabled(self):
"""
Check that the touch zoom functionality is disabled.
Expand Down Expand Up @@ -209,17 +199,17 @@ def has_linkouts(browser, include_site_internal):
Depending on the param passed in, we may want to allow internal (relative) links such as
<a href='/sponsored'></a>
"""
for tag in browser.find_elements_by_css_selector("[href^='http']"):
for tag in browser.find_elements(By.CSS_SELECTOR, "[href^='http']"):
if tag.tag_name != u'link' and not tag.get_attribute('href').startswith('http://127.0.0.1'): #should allow e.g. <link href="styles.css"> and http://127.0.0.1:..
return True
for tag in browser.find_elements_by_css_selector("[href^='//']"):
for tag in browser.find_elements(By.CSS_SELECTOR, "[href^='//']"):
if tag.tag_name != u'link': #should allow e.g. <link href="styles.css">
return True

#all hrefs should now be http or https refs to local stuff. We should double check this
#by looking at the tag.attribute which is fully expanded by selenium/chrome to include http
#but we should exclude all page-local links (i.e. beginning with #)
for tag in browser.find_elements_by_css_selector('[href]:not([href^="#"])'):
for tag in browser.find_elements(By.CSS_SELECTOR, '[href]:not([href^="#"])'):
if tag.tag_name != u'link':
if include_site_internal:
return True
Expand All @@ -234,13 +224,13 @@ def has_linkouts(browser, include_site_internal):
def web2py_date_accessed(browser):
#assumes that we have injected the access date into a meta element called 'date_accessed'
#using the web2py code {{response.meta.date_accessed = request.now}}
return datetime.strptime(browser.find_element_by_xpath("//meta[@name='date_accessed']").get_attribute("content"), date_format)
return datetime.strptime(browser.find_element(By.XPATH, "//meta[@name='date_accessed']").get_attribute("content"), date_format)

def web2py_viewname_contains(browser, expected_view):
#Checks if we have injected the view name into a meta element called 'viewfile'
#using the web2py code {{response.meta.viewfile = response.view}}
try:
return expected_view in browser.find_element_by_xpath("//meta[@name='viewfile']").get_attribute("content")
return expected_view in browser.find_element(By.XPATH, "//meta[@name='viewfile']").get_attribute("content")
except NoSuchElementException:
return False

Expand Down
43 changes: 6 additions & 37 deletions tests/functional/sponsorship/sponsorship_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
# -*- coding: utf-8 -*-
import os
import requests
from nose import tools
from time import sleep

from selenium import webdriver #to fire up a duplicate page

Expand All @@ -17,7 +15,7 @@ class SponsorshipTest(FunctionalTest):
"""

@classmethod
def setUpClass(self):
def setup_class(self):
"""
When setting up the sponsorship pages testing suite we have to create a new appconfig.ini file
and point out web2py instance at it so that we can adjust maintenance_mins and allow_sponsorship
Expand All @@ -33,7 +31,7 @@ def setUpClass(self):
if line.lstrip().startswith("[sponsorship]"):
test.write("maintenance_mins = {}\n".format(self.maintenance_mins))
test.write("allow_sponsorship = {}\n".format(self.allow_sponsorship))
super().setUpClass()
super().setup_class()

#Now get s, from the "normal" OZ viewer, from a museum display,
#from a partner view and also a test case where all links are banned
Expand All @@ -58,13 +56,12 @@ def setUpClass(self):
}

@classmethod
def tearDownClass(self):
super().tearDownClass()
def teardown_class(self):
super().teardown_class()
print(">> removing temporary appconfig")
os.remove(self.appconfig_loc)

@tools.nottest
def test_ott(self, extra_assert_tests, ott, extra_assert_tests_from_another_browser=None, browser=None):
def check_ott(self, extra_assert_tests, ott, extra_assert_tests_from_another_browser=None, browser=None):
"""
Test the 4 separate urls, each viewing the same page linked from a different place
(e.g. from the OZ tree viewer versus the min website, vs a museum display)
Expand All @@ -85,7 +82,7 @@ def test_ott(self, extra_assert_tests, ott, extra_assert_tests_from_another_brow
#still forwards to the same page
print(" ... also testing same pages from an alternative browser ...", end="", flush=True)
alt_browser = webdriver.Chrome()
self.test_ott(extra_assert_tests_from_another_browser, ott, None, alt_browser)
self.check_ott(extra_assert_tests_from_another_browser, ott, None, alt_browser)
alt_browser.quit()
#check all the alternative representations too
print("|w2p", end="", flush=True)
Expand All @@ -107,29 +104,7 @@ def test_ott(self, extra_assert_tests, ott, extra_assert_tests_from_another_brow
assert has_linkouts(browser, include_site_internal=True) == False
assert self.zoom_disabled()
print(" ", end="", flush=True)

@tools.nottest
def test_md_sandbox(self, ott):
"""
Follow any links from the museum display page and collect a list. If any are external, return False
"""
self.browser.get(self.urls['treeviewer_md'](ott))
#Although any element can potentially link out to another page using javascript,
# the most likely elements that will cause a sandbox escape on our own page
# are <a href=...> <form action=...> <area href=...>, or <button onclick=...>
# For external pages (e.g. wikipages) we shoud ensure that JS is stripped.
def first_external_link(self, already_followed):
"""
Recurse through possible links from this page until we find an external one
in which case we can return False, or
"""
return

for elem in self.browser.find_elements_by_tag_name('a'):
href = elem.get_attribute('href')


@tools.nottest
def never_looked_at_ottname(self):
"""
Find an unpopular species that has never been looked at (i.e. does not have an entry in the reservations table
Expand All @@ -151,7 +126,6 @@ def never_looked_at_ottname(self):
db_cursor.close()
return ott, sciname

@tools.nottest
def delete_reservation_entry(self, ott, name, email=test_email):
"""
Warning: this will REMOVE data. Make sure that this is definitely one of the previously not looked at species
Expand All @@ -170,30 +144,26 @@ def delete_reservation_entry(self, ott, name, email=test_email):
db_cursor.close()
return n_rows

@tools.nottest
def invalid_ott(self):
"""
Give an invalid OTT. We should also test for 'species' with no space in the name, but we can't be guaranteed
that there will be any of these
"""
return -1

@tools.nottest
def banned_unsponsored_ott(self):
"""
Human ott is always banned, never sponsored
"""
return self.humanOTT

@tools.nottest
def banned_sponsored_ott(self):
"""
We could possibly pick pandas here, but we are not assured that they will be sponsored on all sites
"""
raise NotImplementedError


@tools.nottest
def sponsored_ott(self):
"""
We might also want to test a sponsored banned species here, like the giant panda
Expand All @@ -206,7 +176,6 @@ def sponsored_ott(self):
else:
return sponsored[0]['OTT_ID']

@tools.nottest
def visit_data(self, ott):
"""
Return the num_views, last_view and the reserve_time for this ott
Expand Down
Loading

0 comments on commit a01594f

Please sign in to comment.