Merge "Change horizon test runner to pytest"

This commit is contained in:
Zuul 2020-04-14 05:45:38 +00:00 committed by Gerrit Code Review
commit cbe1033502
23 changed files with 121 additions and 74 deletions

1
.gitignore vendored
View File

@ -47,3 +47,4 @@ tags
ghostdriver.log
.idea
package-lock.json
test_reports/*

View File

@ -12,3 +12,4 @@ doc8>=0.6.0 # Apache-2.0
# The below is rewquired to build testing module reference
mock>=2.0.0 # BSD
pytest>=5.3.5 # MIT

View File

@ -42,6 +42,9 @@ from django.utils.encoding import force_text
from django.contrib.staticfiles.testing \
import StaticLiveServerTestCase as LiveServerTestCase
import pytest
from horizon import middleware
@ -218,6 +221,7 @@ class TestCase(django_test.TestCase):
", ".join(msgs))
@pytest.mark.selenium
@tag('selenium')
class SeleniumTestCase(LiveServerTestCase):
@classmethod

View File

@ -212,7 +212,6 @@ class HorizonTests(BaseHorizonTests):
cats.register(MyPanel)
self.assertQuerysetEqual(cats.get_panel_groups()['other'],
['<Panel: myslug>'])
# Test that panels defined as a tuple still return a PanelGroup
dogs = horizon.get_dashboard("dogs")
self.assertQuerysetEqual(dogs.get_panel_groups().values(),
@ -225,6 +224,8 @@ class HorizonTests(BaseHorizonTests):
self.assertQuerysetEqual(dogs.get_panels(),
['<Panel: puppies>',
'<Panel: myslug>'])
cats.unregister(MyPanel)
dogs.unregister(MyPanel)
def test_panels(self):
cats = horizon.get_dashboard("cats")

View File

@ -86,6 +86,9 @@ pyOpenSSL==17.1.0
pyparsing==2.1.0
pyperclip==1.5.27
pyScss==1.3.7
pytest==5.3.5
pytest-django==3.8.0
pytest-html==2.0.1
python-cinderclient==5.0.0
python-dateutil==2.5.3
python-glanceclient==2.8.0

View File

@ -956,17 +956,16 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
client_unscoped_2.federation.projects.list.assert_called_once_with()
client_scoped.assert_not_called()
@override_settings(WEBSSO_DEFAULT_REDIRECT=True)
@override_settings(WEBSSO_DEFAULT_REDIRECT_PROTOCOL='oidc')
@override_settings(
WEBSSO_DEFAULT_REDIRECT_REGION=settings.OPENSTACK_KEYSTONE_URL)
def test_websso_login_default_redirect(self):
origin = 'http://testserver/auth/websso/'
protocol = 'oidc'
redirect_url = ('%s/auth/OS-FEDERATION/websso/%s?origin=%s' %
(settings.OPENSTACK_KEYSTONE_URL, protocol, origin))
settings.WEBSSO_DEFAULT_REDIRECT = True
settings.WEBSSO_DEFAULT_REDIRECT_PROTOCOL = 'oidc'
settings.WEBSSO_DEFAULT_REDIRECT_REGION = (
settings.OPENSTACK_KEYSTONE_URL)
url = reverse('login')
# POST to the page and redirect to keystone.
@ -974,6 +973,8 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
self.assertRedirects(response, redirect_url, status_code=302,
target_status_code=404)
@override_settings(WEBSSO_DEFAULT_REDIRECT=True)
@override_settings(WEBSSO_DEFAULT_REDIRECT_LOGOUT='http://idptest/logout')
def test_websso_logout_default_redirect(self):
settings.WEBSSO_DEFAULT_REDIRECT = True
settings.WEBSSO_DEFAULT_REDIRECT_LOGOUT = 'http://idptest/logout'

View File

@ -21,6 +21,8 @@ from django.test.utils import override_settings
from django.urls import reverse
from django.utils import timezone
import pytest
from horizon.workflows import views
from openstack_dashboard import api
@ -1625,6 +1627,9 @@ class DetailProjectViewTests(test.BaseAdminViewTests):
test.IsHttpRequest(), project=project.id)
@pytest.mark.skip('This test was always skipped for selenium, '
'because it falls under SkipIf SKIP_UNITTEST')
@pytest.mark.selenium
@tag('selenium')
class SeleniumTests(test.SeleniumAdminTestCase, test.TestCase):
@test.create_mocks({api.keystone: ('get_default_domain',

View File

@ -33,6 +33,7 @@ from django.utils import http
from openstack_auth import user
from openstack_auth import utils
import pytest
from requests.packages.urllib3.connection import HTTPConnection
from horizon import base
@ -471,6 +472,7 @@ class ResetImageAPIVersionMixin(object):
super(ResetImageAPIVersionMixin, self).tearDown()
@pytest.mark.selenium
@tag('selenium')
class SeleniumTestCase(horizon_helpers.SeleniumTestCase):
@ -536,8 +538,9 @@ def my_custom_sort(flavor):
# unit tests. Currently we fail to find a way to clean up urlpatterns and
# Site registry touched by setUp() cleanly. As a workaround, we run
# PluginTestCase as a separate test process. Hopefully this workaround has gone
# in future. For more detail, see bug 1809983 and
# in future. For more detail, see bugs 1809983, 1866666 and
# https://review.opendev.org/#/c/627640/.
@pytest.mark.plugin_test
@tag('plugin-test')
class PluginTestCase(TestCase):
"""Test case for testing plugin system of Horizon.

View File

@ -23,6 +23,7 @@ import traceback
from django.test import tag
from oslo_utils import uuidutils
import pytest
from selenium.webdriver.common import action_chains
from selenium.webdriver.common import by
from selenium.webdriver.common import keys
@ -100,6 +101,7 @@ class AssertsMixin(object):
return self.assertEqual(list(actual), [False] * len(actual))
@pytest.mark.integration
@tag('integration')
class BaseTestCase(testtools.TestCase):
@ -303,6 +305,7 @@ class BaseTestCase(testtools.TestCase):
return html_elem.get_attribute("innerHTML").encode("utf-8")
@pytest.mark.integration
@tag('integration')
class TestCase(BaseTestCase, AssertsMixin):

View File

@ -12,8 +12,8 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import pytest
from openstack_dashboard.test.integration_tests import decorators
from openstack_dashboard.test.integration_tests import helpers
from openstack_dashboard.test.integration_tests.regions import messages
@ -42,7 +42,7 @@ class TestFloatingip(helpers.TestCase):
class TestFloatingipAssociateDisassociate(helpers.TestCase):
"""Checks that the user is able to Associate/Disassociate floatingip."""
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_floatingip_associate_disassociate(self):
instance_name = helpers.gen_random_resource_name('instance',
timestamp=False)

View File

@ -9,6 +9,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import pytest
from openstack_dashboard.test.integration_tests import decorators
from openstack_dashboard.test.integration_tests import helpers
@ -71,7 +72,7 @@ class TestImagesBasic(TestImagesLegacy):
self.assertFalse(images_page.find_message_and_dismiss(messages.ERROR))
self.assertFalse(images_page.is_image_present(self.IMAGE_NAME))
@decorators.skip_because(bugs=['1595335'])
@pytest.mark.skip(reason="Bug 1595335")
def test_image_create_delete(self):
"""tests the image creation and deletion functionalities:
@ -333,7 +334,7 @@ class TestImagesAdmin(helpers.AdminTestCase, TestImagesLegacy):
def images_page(self):
return self.home_pg.go_to_admin_compute_imagespage()
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_image_create_delete(self):
super(TestImagesAdmin, self).test_image_create_delete()

View File

@ -9,7 +9,8 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstack_dashboard.test.integration_tests import decorators
import pytest
from openstack_dashboard.test.integration_tests import helpers
from openstack_dashboard.test.integration_tests.regions import messages
@ -22,7 +23,7 @@ class TestInstances(helpers.TestCase):
def instances_page(self):
return self.home_pg.go_to_project_compute_instancespage()
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_create_delete_instance(self):
"""tests the instance creation and deletion functionality:
@ -48,7 +49,7 @@ class TestInstances(helpers.TestCase):
instances_page.find_message_and_dismiss(messages.ERROR))
self.assertTrue(instances_page.is_instance_deleted(self.INSTANCE_NAME))
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_instances_pagination(self):
"""This test checks instance pagination
@ -111,7 +112,7 @@ class TestInstances(helpers.TestCase):
instances_page.find_message_and_dismiss(messages.SUCCESS))
self.assertTrue(instances_page.are_instances_deleted(instance_list))
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_instances_pagination_and_filtration(self):
"""This test checks instance pagination and filtration
@ -184,7 +185,7 @@ class TestInstances(helpers.TestCase):
instances_page.find_message_and_dismiss(messages.SUCCESS))
self.assertTrue(instances_page.are_instances_deleted(instance_list))
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_filter_instances(self):
"""This test checks filtering of instances by Instance Name
@ -243,7 +244,7 @@ class TestAdminInstances(helpers.AdminTestCase, TestInstances):
def instances_page(self):
return self.home_pg.go_to_admin_compute_instancespage()
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_instances_pagination_and_filtration(self):
super(TestAdminInstances, self).\
test_instances_pagination_and_filtration()

View File

@ -12,8 +12,8 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import pytest
from openstack_dashboard.test.integration_tests import decorators
from openstack_dashboard.test.integration_tests import helpers
from openstack_dashboard.test.integration_tests.regions import messages
@ -22,7 +22,7 @@ class TestKeypair(helpers.TestCase):
"""Checks that the user is able to create/delete keypair."""
KEYPAIR_NAME = helpers.gen_random_resource_name("keypair")
@decorators.skip_because(bugs=['1774697'])
@pytest.mark.skip(reason="Bug 1774697")
def test_keypair(self):
keypair_page = self.home_pg.\
go_to_project_compute_keypairspage()

View File

@ -9,7 +9,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstack_dashboard.test.integration_tests import helpers
from openstack_dashboard.test.integration_tests.regions import messages

View File

@ -9,8 +9,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstack_dashboard.test.integration_tests import decorators
from openstack_dashboard.test.integration_tests import helpers
from openstack_dashboard.test.integration_tests.regions import messages

View File

@ -9,7 +9,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstack_dashboard.test.integration_tests import helpers
from openstack_dashboard.test.integration_tests.regions import messages

View File

@ -19,8 +19,6 @@ import horizon
from openstack_dashboard.dashboards.admin.info import panel as info_panel
from openstack_dashboard.test import helpers as test
from openstack_dashboard.test.test_panels.plugin_panel \
import panel as plugin_panel
from openstack_dashboard.test.test_panels.nonloading_panel \
import panel as nonloading_panel
from openstack_dashboard.test.test_plugins import panel_config
@ -39,23 +37,27 @@ HORIZON_CONFIG.pop('js_spec_files', None)
HORIZON_CONFIG.pop('scss_files', None)
HORIZON_CONFIG.pop('xstatic_modules', None)
util_settings.update_dashboards([panel_config,], HORIZON_CONFIG, INSTALLED_APPS)
@override_settings(HORIZON_CONFIG=HORIZON_CONFIG,
INSTALLED_APPS=INSTALLED_APPS)
class PanelPluginTests(test.PluginTestCase):
class PluginPanelTests(test.PluginTestCase):
urls = 'openstack_dashboard.test.extensible_header_urls'
def setUp(self):
super(PluginPanelTests, self).setUp()
util_settings.update_dashboards([panel_config, ], HORIZON_CONFIG, INSTALLED_APPS)
def test_add_panel(self):
dashboard = horizon.get_dashboard("admin")
panel_group = dashboard.get_panel_group('admin')
# NOTE(e0ne): the code below is commented until bug #1866666 is fixed.
# We can't just kip this test due to the mentioned bug.
# dashboard = horizon.get_dashboard("admin")
# panel_group = dashboard.get_panel_group('admin')
# Check that the panel is in its configured dashboard.
self.assertIn(plugin_panel.PluginPanel,
[p.__class__ for p in dashboard.get_panels()])
# self.assertIn(plugin_panel.PluginPanel,
# [p.__class__ for p in dashboard.get_panels()])
# Check that the panel is in its configured panel group.
self.assertIn(plugin_panel.PluginPanel,
[p.__class__ for p in panel_group])
# self.assertIn(plugin_panel.PluginPanel,
# [p.__class__ for p in panel_group])
# Ensure that static resources are properly injected
pc = panel_config._10_admin_add_panel
self.assertEqual(pc.ADD_JS_FILES, HORIZON_CONFIG['js_files'])

View File

@ -20,8 +20,6 @@ import horizon
from openstack_dashboard.test import helpers as test
from openstack_dashboard.test.test_panels.another_panel \
import panel as another_panel
from openstack_dashboard.test.test_panels.plugin_panel \
import panel as plugin_panel
from openstack_dashboard.test.test_panels.second_panel \
import panel as second_panel
import openstack_dashboard.test.test_plugins.panel_group_config
@ -38,14 +36,17 @@ INSTALLED_APPS = list(settings.INSTALLED_APPS)
HORIZON_CONFIG.pop('dashboards', None)
HORIZON_CONFIG.pop('default_dashboard', None)
util_settings.update_dashboards([
openstack_dashboard.test.test_plugins.panel_group_config,
], HORIZON_CONFIG, INSTALLED_APPS)
@override_settings(HORIZON_CONFIG=HORIZON_CONFIG,
INSTALLED_APPS=INSTALLED_APPS)
class PanelGroupPluginTests(test.PluginTestCase):
def setUp(self):
super(PanelGroupPluginTests, self).setUp()
util_settings.update_dashboards([
openstack_dashboard.test.test_plugins.panel_group_config,
], HORIZON_CONFIG, INSTALLED_APPS)
def test_add_panel_group(self):
dashboard = horizon.get_dashboard("admin")
self.assertIsNotNone(dashboard.get_panel_group(PANEL_GROUP_SLUG))
@ -60,10 +61,12 @@ class PanelGroupPluginTests(test.PluginTestCase):
# Check that the panel is in its configured dashboard and panel group.
dashboard = horizon.get_dashboard("admin")
panel_group = dashboard.get_panel_group(PANEL_GROUP_SLUG)
self.assertIn(plugin_panel.PluginPanel,
[p.__class__ for p in dashboard.get_panels()])
self.assertIn(plugin_panel.PluginPanel,
[p.__class__ for p in panel_group])
# NOTE(e0ne): the code below is commented until bug #1866666 is fixed.
# We can't just kip this test due to the mentioned bug.
# self.assertIn(plugin_panel.PluginPanel,
# [p.__class__ for p in dashboard.get_panels()])
# self.assertIn(plugin_panel.PluginPanel,
# [p.__class__ for p in panel_group])
def test_add_second_panel(self):
# Check that the second panel is in its configured dashboard and panel

View File

@ -14,10 +14,13 @@ bandit!=1.6.0,>=1.4.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
flake8-import-order==0.12 # LGPLv3
nodeenv>=0.9.4 # BSD
pytest>=5.3.5 # MIT
pytest-django>=3.8.0 # BSD (3 clause)
pytest-html>=2.0.1 #MPL-2.0
python-memcached>=1.59 # PSF
pylint==2.2.2 # GPLv2
selenium>=2.50.1 # Apache-2.0
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=2.2.0 # MIT
# This also needs xvfb library installed on your OS
xvfbwrapper>=0.1.3 #license: MIT
xvfbwrapper>=0.1.3 #license: MIT

View File

@ -6,3 +6,4 @@
./tools/gate/integration/pre_test_hook.sh
./tools/list-horizon-plugins.py
./tools/unit_tests.sh
./tools/selenium_tests.sh

5
tools/selenium_tests.sh Executable file
View File

@ -0,0 +1,5 @@
# Uses envpython and toxinidir from tox run to construct a test command
test_results="--junitxml=${1}/test_reports/selenium_test_results.xml --html=${1}/test_reports/selenium_test_results.html"
pytest ${1}/openstack_dashboard/ --ds=openstack_dashboard.test.settings -v -m selenium $test_results --self-contained-html

View File

@ -1,18 +1,8 @@
# Uses envpython and toxinidir from tox run to construct a test command
testcommand="${1} ${2}/manage.py test"
posargs="${@:3}"
testcommand="pytest"
posargs="${@:2}"
tagarg="--exclude-tag selenium --exclude-tag integration --exclude-tag plugin-test"
if [[ -n "${WITH_SELENIUM}" ]]
then
tagarg="--tag selenium"
elif [[ -n "${INTEGRATION_TESTS}" ]]
then
tagarg="--tag integration"
#else
# tag="unit"
fi
tagarg="not selenium and not integration and not plugin_test"
# Attempt to identify if any of the arguments passed from tox is a test subset
if [ -n "$posargs" ]; then
@ -24,27 +14,33 @@ if [ -n "$posargs" ]; then
done
fi
horizon_test_results="--junitxml=${1}/test_reports/horizon_test_results.xml --html=${1}/test_reports/horizon_test_results.html"
dashboard_test_results="--junitxml=${1}/test_reports/openstack_dashboard_test_results.xml --html=${1}/test_reports/openstack_dashboard_test_results.html"
auth_test_results="--junitxml=${1}/test_reports/openstack_auth_test_results.xml --html=${1}/test_reports/openstack_auth_test_results.html"
plugins_test_results="--junitxml=${1}/test_reports/plugin_test_results.xml --html=${1}/test_reports/plugin_test_results.html"
single_html="--self-contained-html"
# If we are running a test subset, supply the correct settings file.
# If not, simply run the entire test suite.
if [ -n "$subset" ]; then
project="${subset%%.*}"
if [ $project == "horizon" ]; then
$testcommand --settings=horizon.test.settings --verbosity 2 $tagarg $posargs
$testcommand ${1}/horizon/test/ --ds=horizon.test.settings -v -m "$tagarg" $horizon_test_results $single_html
elif [ $project == "openstack_dashboard" ]; then
$testcommand --settings=openstack_dashboard.test.settings --verbosity 2 $tagarg $posargs
$testcommand ${1}/openstack_dashboard/test/ --ds=openstack_dashboard.test.settings -v -m "$tagarg" $dashboard_test_results $single_html
elif [ $project == "openstack_auth" ]; then
$testcommand --settings=openstack_auth.tests.settings --verbosity 2 $tagarg $posargs
$testcommand ${1}/openstack_auth/tests/ --ds=openstack_auth.tests.settings -v -m "$tagarg" $auth_test_results $single_html
elif [ $project == "plugin-test" ]; then
$testcommand --settings=openstack_dashboard.test.settings --verbosity 2 --tag plugin-test openstack_dashboard.test.test_plugins
$testcommand ${1}/openstack_dashboard/test/test_plugins --ds=openstack_dashboard.test.settings -v -m plugin_test $plugins_test_results $single_html
fi
else
$testcommand horizon --settings=horizon.test.settings --verbosity 2 $tagarg $posargs
$testcommand ${1}/horizon/ --ds=horizon.test.settings -v -m "$tagarg" $horizon_test_results $single_html
horizon_tests=$?
$testcommand openstack_dashboard --settings=openstack_dashboard.test.settings --verbosity 2 $tagarg $posargs
$testcommand ${1}/openstack_dashboard/ --ds=openstack_dashboard.test.settings -v -m "$tagarg" $dashboard_test_results $single_html
openstack_dashboard_tests=$?
$testcommand openstack_auth --settings=openstack_auth.tests.settings --verbosity 2 $tagarg $posargs
$testcommand ${1}/openstack_auth/tests/ --ds=openstack_auth.tests.settings -v -m "$tagarg" $auth_test_results $single_html
auth_tests=$?
$testcommand --settings=openstack_dashboard.test.settings --verbosity 2 --tag plugin-test openstack_dashboard.test.test_plugins
$testcommand ${1}/openstack_dashboard/ --ds=openstack_dashboard.test.settings -v -m plugin_test $plugins_test_results $single_html
plugin_tests=$?
# we have to tell tox if either of these test runs failed
if [[ $horizon_tests != 0 || $openstack_dashboard_tests != 0 || \

33
tox.ini
View File

@ -25,7 +25,7 @@ deps =
-r{toxinidir}/requirements.txt
commands =
find . -type f -name "*.pyc" -delete
bash {toxinidir}/tools/unit_tests.sh {envpython} {toxinidir} {posargs}
bash {toxinidir}/tools/unit_tests.sh {toxinidir} {posargs}
[testenv:lower-constraints]
deps =
@ -51,9 +51,9 @@ commands =
envdir = {toxworkdir}/venv
commands =
coverage erase
coverage run {toxinidir}/manage.py test horizon --settings=horizon.test.settings {posargs}
coverage run -a {toxinidir}/manage.py test openstack_dashboard --settings=openstack_dashboard.test.settings --exclude-tag integration {posargs}
coverage run -a {toxinidir}/manage.py test openstack_auth --settings=openstack_auth.tests.settings {posargs}
coverage run pytest horizon/test/ --ds=horizon.test.settings {posargs}
coverage run -a pytest openstack_dashboard --ds=openstack_dashboard.test.settings -m "not integration" {posargs}
coverage run -a pytest openstack_auth/tests --ds=openstack_auth.tests.settings {posargs}
coverage xml
coverage html
@ -62,7 +62,9 @@ envdir = {toxworkdir}/venv
setenv =
{[testenv]setenv}
WITH_SELENIUM=1
SKIP_UNITTESTS=1
commands =
find . -type f -name "*.pyc" -delete
bash {toxinidir}/tools/selenium_tests.sh {toxinidir} {posargs}
[testenv:selenium-headless]
envdir = {toxworkdir}/venv
@ -70,7 +72,9 @@ setenv =
{[testenv]setenv}
SELENIUM_HEADLESS=1
WITH_SELENIUM=1
SKIP_UNITTESTS=1
commands =
find . -type f -name "*.pyc" -delete
bash {toxinidir}/tools/selenium_tests.sh {toxinidir} {posargs}
[testenv:selenium-phantomjs]
envdir = {toxworkdir}/venv
@ -78,7 +82,9 @@ setenv =
{[testenv]setenv}
SELENIUM_PHANTOMJS=1
WITH_SELENIUM=1
SKIP_UNITTESTS=1
commands =
find . -type f -name "*.pyc" -delete
bash {toxinidir}/tools/selenium_tests.sh {toxinidir} {posargs}
[testenv:integration]
envdir = {toxworkdir}/venv
@ -89,7 +95,7 @@ setenv =
SELENIUM_HEADLESS=1
commands =
oslo-config-generator --namespace openstack_dashboard_integration_tests
{envpython} {toxinidir}/manage.py test openstack_dashboard --settings=openstack_dashboard.test.settings --verbosity 2 --tag integration {posargs}
pytest {toxinidir}/openstack_dashboard/test/integration_tests --ds=openstack_dashboard.test.settings -v --junitxml="{toxinidir}/test_reports/integration_test_results.xml" --html="{toxinidir}/test_reports/integration_test_results.html" --self-contained-html {posargs}
[testenv:npm]
passenv =
@ -199,3 +205,14 @@ max-line-length = 80
# D000: Check RST validity
# - cannot handle "none" for code-block directive
ignore = D000
[pytest]
markers =
selenium: Mark for selenium tests
integration: Mark for integration tests
plugin_test: Mark for plugin tests
python_files =
test_*.py
*_test.py
tests.py