[tox]
minversion = 4.4
envlist = pep8,py3,releasenotes,npm,py3-dj42

[testenv]
usedevelop = True
setenv =
  PYTHONDONTWRITEBYTECODE = 1
  # TODO(amotoki): Consider dropping PendingDeprecationWarning
  # handle deprecated Django features earlier.
  PYTHONWARNINGS = once,ignore::PendingDeprecationWarning

allowlist_externals =
  bash
  find
  {toxinidir}/tools/pip.sh
deps =
  -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2025.2}
  -r{toxinidir}/test-requirements.txt
  -r{toxinidir}/requirements.txt
  # Unit test for a local hacking rule requires hacking.
  hacking>=7.0.0,<7.1.0 # Apache-2.0
  pip==25.3  # Needed for build-constraints.txt
  setuptools<82  # Due to deprecation of pkg_resources
commands =
  dj42: pip install django>=4.2,<4.3
  find . -type f -name "*.pyc" -delete
  bash {toxinidir}/tools/unit_tests.sh {toxinidir} {posargs}
install_command = {toxinidir}/tools/pip.sh --build-constraint={toxinidir}/build-constraints.txt {opts} {packages}

[testenv:venv]
commands = {posargs}

[testenv:pep8]
deps =
  {[testenv]deps}
  flake8-import-order>=0.18.0,<0.19.0 # LGPLv3
  pylint==3.3.1 # GPLv2
  bandit[baseline]>=1.7.7 # Apache-2.0
  setuptools<82
setenv =
  DJANGO_SETTINGS_MODULE=openstack_dashboard.test.settings
commands =
  flake8 {posargs}
  {envpython} {toxinidir}/manage.py extract_messages --verbosity 0 --check-only
  bash {toxinidir}/tools/find_executables.sh
  pylint -j 4 --verbose --rcfile=.pylintrc -f colorized openstack_dashboard openstack_auth horizon

[testenv:cover]
allowlist_externals =
  bash
  {toxinidir}/tools/pip.sh
commands =
  coverage erase
  bash {toxinidir}/tools/unit_tests.sh --coverage {toxinidir}
  coverage xml
  coverage html -d coverage
  coverage report --show-missing --skip-covered

[testenv:selenium]
setenv =
  {[testenv]setenv}
  WITH_SELENIUM=1
commands =
  find . -type f -name "*.pyc" -delete
  bash {toxinidir}/tools/selenium_tests.sh {toxinidir} {posargs}

[testenv:selenium-headless]
setenv =
  {[testenv]setenv}
  SELENIUM_HEADLESS=True
  WITH_SELENIUM=1
commands =
  find . -type f -name "*.pyc" -delete
  bash {toxinidir}/tools/selenium_tests.sh {toxinidir} {posargs}

[testenv:selenium-phantomjs]
setenv =
  {[testenv]setenv}
  SELENIUM_PHANTOMJS=1
  WITH_SELENIUM=1
commands =
  find . -type f -name "*.pyc" -delete
  bash {toxinidir}/tools/selenium_tests.sh {toxinidir} {posargs}

[testenv:integration]
# Run integration tests only
passenv =
  DISPLAY
  FFMPEG_INSTALLED
  XAUTHORITY
setenv =
  INTEGRATION_TESTS=1
  SELENIUM_HEADLESS=True
commands =
  oslo-config-generator --namespace openstack_dashboard_integration_tests
  pytest --ds=openstack_dashboard.test.settings -v -x --junitxml="{toxinidir}/test_reports/integration_test_results.xml" --html="{toxinidir}/test_reports/integration_test_results.html" --self-contained-html {posargs:{toxinidir}/openstack_dashboard/test/integration_tests}

[testenv:integration-pytest]
# Run pytest integration tests only
passenv =
  DISPLAY
  FFMPEG_INSTALLED
  XAUTHORITY
setenv =
  SELENIUM_HEADLESS=True
commands =
  oslo-config-generator --namespace openstack_dashboard_integration_tests
  pytest -x -v -k "not federation" --junitxml="{toxinidir}/test_reports/integration_pytest_results.xml" --html="{toxinidir}/test_reports/integration_pytest_results.html" --self-contained-html {posargs:{toxinidir}/openstack_dashboard/test/selenium/integration}

[testenv:ui-pytest]
# Run pytest ui tests only
passenv =
  DISPLAY
  FFMPEG_INSTALLED
  XAUTHORITY
setenv =
  SELENIUM_HEADLESS=True
commands =
  oslo-config-generator --namespace openstack_dashboard_integration_tests
  pytest --ds=openstack_dashboard.settings -v --junitxml="{toxinidir}/test_reports/integration_uitest_results.xml" --html="{toxinidir}/test_reports/integration_uitest_results.html" --self-contained-html {posargs:{toxinidir}/openstack_dashboard/test/selenium/ui}

[testenv:npm]
passenv =
  HOME
  DISPLAY
commands =
  nodeenv -p
  npm install
  npm run {posargs:test}

[testenv:tests_system_packages]
# Provide an environment for system packagers that dont want anything from pip
# Any extra deps needed for this env can be passed by setting TOX_EXTRA_DEPS
sitepackages = True
passenv =
  TOX_EXTRA_DEPS
  http_proxy
  HTTP_PROXY
  https_proxy
  HTTPS_PROXY
  no_proxy
  NO_PROXY
# Sets deps to an empty list so nothing is installed from pip
deps =
commands =
  pip install -U {env:TOX_EXTRA_DEPS:}
  {[testenv]commands}

[testenv:docs]
# We need to install horizon dependecies to build module references
deps =
  -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2025.2}
  -r{toxinidir}/requirements.txt
  -r{toxinidir}/doc/requirements.txt
commands =
  sphinx-build -j auto -W -b html doc/source doc/build/html
  doc8 doc/source
  docstheme-build-translated.sh
setenv =
  SPHINX_WARNINGS_TRANS=1

[testenv:venv-docs]
deps = {[testenv:docs]deps}
commands = {posargs}

[testenv:pdf-docs]
deps = {[testenv:docs]deps}
allowlist_externals =
  make
  {toxinidir}/tools/pip.sh
commands =
  sphinx-build -j auto -W -b latex doc/source doc/build/pdf
  make -C doc/build/pdf

[testenv:releasenotes]
deps = {[testenv:docs]deps}
commands =
  sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
  doc8 doc/source

[testenv:manage]
commands = {envpython} {toxinidir}/manage.py {posargs}

[testenv:runserver]
commands =
    {envpython} {toxinidir}/manage.py collectstatic -v 0 -c --noinput
    {envpython} {toxinidir}/manage.py runserver -v 0 {posargs}

[testenv:translations]
deps =
  {[testenv]deps}
commands =
    {envpython} {toxinidir}/manage.py extract_messages -v 0 --traceback
    {envpython} {toxinidir}/manage.py compilemessages -v 0 --traceback

[testenv:bandit]
deps = {[testenv:pep8]deps}
commands = bandit -r horizon openstack_auth openstack_dashboard -n5 -x tests -ll

[testenv:bandit-baseline]
deps = {[testenv:pep8]deps}
commands = bandit-baseline -r horizon openstack_auth openstack_dashboard -n5 -x tests -ii -ll

[flake8]
filename = *.py,django.wsgi
exclude = .git,.tox,dist,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py,*/local/*,*/test/test_plugins/*,.ropeproject,node_modules,openstack_dashboard/enabled/*,horizon_wsgi.py
# W504 line break after binary operator
# (W503 and W504 are incompatible and we need to choose one of them.
#  Existing codes follows W503, so we disable W504.)
# F405 TEMPLATES may be undefined, or defined from star imports
# (because it is not easy to avoid this in openstack_dashboard.test.settings)
# I201 Missing newline between import groups.
# I202 Additional newline in a group of imports.
ignore = W504,F405,I201,I202
# Enable the following hacking rules which are disabled by default
# H106 Do not put vim configuration in source files.
# H203 Use assertIs(Not)None to check for None.
# H204 Use assert(Not)Equal to check for equality.
# H205 Use assert(Greater|Less)(Equal) for comparison.
# H904 Delay string interpolations at logging calls.
enable-extensions=H106,H203,H204,H205,H904
max-complexity = 20
max_line_length = 80

# flake8-import-order configurations
import-order-style = pep8
application-import-names = horizon,openstack_dashboard

[flake8:local-plugins]
extension =
  M322 = horizon.hacking.checks:no_mutable_default_args

[doc8]
# File extensions to check
extensions = .rst, .yaml
# Maximal line length should be 80 but we have some overlong lines.
# Let's not get far more in.
max-line-length = 80
# Disable some doc8 checks:
# D000: Check RST validity
# - cannot handle "none" for code-block directive
# D001: Line too long
# - Ignore since we allow lines in excess of 79 characters.
ignore = D000,D001

[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
