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

Draft: replace nosetests with pytest #936

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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 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'))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was redirecting to a non-existent page

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):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spelling of setup/teardown stuff changes across the board

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to choose a solution for running the functional tests on beta.onezoom.org, though it doesn't have to look like this

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()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also updated to selenium 4 whilst we're here, so lots of 'find_element_by...' stuff has changed to the new interface

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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed some functions so that things we actually want collected as tests start in 'test_' and those that we don't want collected as tests do not

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what this unused function was about. Looks like just an unfinished idea for a test?

@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
Loading