From 6ef902cc36004fa3f86206d0bfc4778c9a44e7c3 Mon Sep 17 00:00:00 2001 From: Artur Barseghyan Date: Sun, 20 Nov 2022 21:24:43 +0100 Subject: [PATCH 1/3] Prepare 0.19.5. Enable accidentally forgotten permission in the class-based Dashboard view --- CHANGELOG.rst | 6 ++++++ setup.py | 2 +- src/fobi/__init__.py | 2 +- src/fobi/views/class_based.py | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f6e4fb6c8..d9181ecc8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,12 @@ are used for versioning (schema follows below): 0.3.4 to 0.4). - All backwards incompatible changes are mentioned in this document. +0.19.5 +------ +2022-11-20 + +- Enable accidentally forgotten login-required permission on the Dashboard view. + 0.19.4 ------ 2022-08-08 diff --git a/setup.py b/setup.py index e1737ba20..2cc230f02 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from distutils.version import LooseVersion from setuptools import setup, find_packages -version = "0.19.4" +version = "0.19.5" # *************************************************************************** # ************************** Django version ********************************* diff --git a/src/fobi/__init__.py b/src/fobi/__init__.py index 24650a531..3d2a2fc34 100644 --- a/src/fobi/__init__.py +++ b/src/fobi/__init__.py @@ -1,5 +1,5 @@ __title__ = "django-fobi" -__version__ = "0.19.4" +__version__ = "0.19.5" __author__ = "Artur Barseghyan " __copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" diff --git a/src/fobi/views/class_based.py b/src/fobi/views/class_based.py index abd689fa8..05d2c1a49 100644 --- a/src/fobi/views/class_based.py +++ b/src/fobi/views/class_based.py @@ -239,7 +239,7 @@ def run_after_plugin_entry_delete(self, request, form_entry_id): # ***************************************************************************** -class DashboardView(ListView): +class DashboardView(PermissionMixin, ListView): """Dashboard view.""" template_name = None From 12df02e6bcb51c9030a9bde8a96ff941cc716871 Mon Sep 17 00:00:00 2001 From: Artur Barseghyan Date: Tue, 29 Nov 2022 00:34:04 +0100 Subject: [PATCH 2/3] Feature/Add Django 4.1 support (#301) * Django 4.1 support. * Fix `NullBooleanField` DRF deprecation. * Soft pin `importlib.metadata` * Obtain `chromedriver-py` using `get-chromedriver-py`. * Fix `selenium` deprecation errors. * Use testing email backend. * Install some linux deps in GitHub CI. --- .github/workflows/optional.yml | 77 ++++ .github/workflows/test.yml | 33 +- CHANGELOG.rst | 9 + Makefile | 41 ++ README.rst | 4 +- docker/backend/Dockerfile | 17 +- examples/requirements/captcha.txt | 2 + examples/requirements/common.in | 1 + examples/requirements/demo.txt | 1 + examples/requirements/dev.txt | 3 +- examples/requirements/django_2_2.txt | 1 + examples/requirements/django_3_0.txt | 1 + examples/requirements/django_3_1.txt | 1 + examples/requirements/django_3_2.txt | 1 + examples/requirements/django_4_1.in | 15 + examples/requirements/django_4_1.txt | 349 ++++++++++++++++++ examples/requirements/docs.txt | 1 + examples/requirements/test.in | 3 +- examples/requirements/testing.txt | 1 + examples/simple/foo/views.py | 2 - examples/simple/settings/base.py | 7 +- examples/simple/urls/__init__.py | 1 + .../simple/{urls.py => urls/class_based.py} | 15 +- examples/simple/urls/function_based.py | 158 ++++++++ scripts/compile_requirements.sh | 1 + setup.py | 5 +- src/fobi/__init__.py | 2 +- src/fobi/admin.py | 18 +- src/fobi/base.py | 2 +- .../contrib/apps/drf_integration/dynamic.py | 5 +- .../form_elements/fields/null_boolean/base.py | 5 +- .../apps/drf_integration/serializers.py | 3 +- .../contrib/apps/drf_integration/views.py | 2 - .../mezzanine_integration/page_processors.py | 1 - .../content/content_image/base.py | 2 +- .../content/content_image_url/base.py | 2 +- .../content/content_markdown/base.py | 2 +- .../content/content_text/base.py | 2 +- .../content/content_video/base.py | 2 +- .../form_elements/fields/slider/base.py | 1 - .../plugins/form_handlers/db_store/views.py | 3 - src/fobi/dynamic.py | 2 +- src/fobi/migrations/0010_formwizardhandler.py | 1 - src/fobi/tests/__init__.py | 17 - src/fobi/tests/base.py | 16 +- src/fobi/tests/data.py | 9 +- .../tests/test_browser_build_dynamic_forms.py | 280 ++++++++++---- src/fobi/tests/test_core.py | 7 +- src/fobi/tests/test_dynamic_forms.py | 8 +- src/fobi/tests/test_feincms_integration.py | 19 +- .../tests/test_form_importers_mailchimp.py | 8 +- src/fobi/tests/test_sortable_dict.py | 7 +- src/fobi/utils.py | 2 +- src/fobi/views/function_based.py | 1 - src/fobi/wizard/views/dynamic.py | 1 - tox.ini | 12 +- 56 files changed, 986 insertions(+), 206 deletions(-) create mode 100644 .github/workflows/optional.yml create mode 100644 examples/requirements/django_4_1.in create mode 100644 examples/requirements/django_4_1.txt create mode 100644 examples/simple/urls/__init__.py rename examples/simple/{urls.py => urls/class_based.py} (95%) create mode 100644 examples/simple/urls/function_based.py diff --git a/.github/workflows/optional.yml b/.github/workflows/optional.yml new file mode 100644 index 000000000..2c8200090 --- /dev/null +++ b/.github/workflows/optional.yml @@ -0,0 +1,77 @@ +name: Optional + +on: [push, pull_request] + +jobs: + test: + # Service containers to run with `container-job` + services: + # Label used to access the service container + postgres: + # Docker Hub image + image: postgres + # Provide the password for postgres + env: + POSTGRES_PASSWORD: test + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 4 + matrix: + include: + - python-version: '3.11' + requirements: django_4_0 + tox_env: py311-django40 + + - python-version: '3.11' + requirements: django_4_1 + tox_env: py311-django41 + + steps: + - name: Install libxml2 and libxslt + run: sudo apt-get install -y libxml2-dev libxslt-dev + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - uses: nanasess/setup-chromedriver@master + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install pip-tools + pip-compile examples/requirements/${{ matrix.requirements }}.in + pip install -r examples/requirements/${{ matrix.requirements }}.txt + pip-compile examples/requirements/test.in + pip install -r examples/requirements/test.txt + - name: Run Tests + run: tox -e ${{ matrix.tox_env }} + env: + POSTGRES_HOST: localhost + POSTGRES_PORT: 5432 + - name: Coveralls + uses: AndreMiras/coveralls-python-action@develop + with: + parallel: true + flag-name: Run Tests + + coveralls_finish: + needs: test + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + GITHUB_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + uses: AndreMiras/coveralls-python-action@develop + with: + parallel-finished: true + debug: True diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d246f4ef8..0610dab49 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: test +name: Test on: [push, pull_request] @@ -23,6 +23,7 @@ jobs: - 5432:5432 runs-on: ubuntu-latest strategy: + fail-fast: false max-parallel: 4 matrix: include: @@ -84,18 +85,35 @@ jobs: - python-version: 3.9 requirements: django_4_0 tox_env: py39-django40 + - python-version: '3.10' + requirements: django_4_0 + tox_env: py310-django40 +# - python-version: '3.11' +# requirements: django_4_0 +# tox_env: py311-django40 + + - python-version: 3.8 + requirements: django_4_1 + tox_env: py38-django41 + - python-version: 3.9 + requirements: django_4_1 + tox_env: py39-django41 + - python-version: '3.10' + requirements: django_4_1 + tox_env: py310-django41 +# - python-version: '3.11' +# requirements: django_4_1 +# tox_env: py311-django41 steps: + - name: Install libxml2 and libxslt + run: sudo apt-get install -y libxml2-dev libxslt-dev - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - uses: nanasess/setup-chromedriver@master - with: - # Optional: do not specify to match Chrome's version - chromedriver-version: '102.0.5005.61' - - name: Install Dependencies run: | python -m pip install --upgrade pip @@ -110,6 +128,8 @@ jobs: POSTGRES_HOST: localhost POSTGRES_PORT: 5432 - name: Coveralls + id: coveralls-setup + continue-on-error: true uses: AndreMiras/coveralls-python-action@develop with: parallel: true @@ -120,6 +140,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Coveralls Finished + id: coveralls-finish + continue-on-error: true + if: strategy.steps.coveralls-setup.outcome == 'success' env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d9181ecc8..8be7f15ed 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,15 @@ are used for versioning (schema follows below): 0.3.4 to 0.4). - All backwards incompatible changes are mentioned in this document. +0.19.6 +------ +2022-11-28 + +- Tested against Python 3.10 and 3.11. Note that ATM, Python 3.11 tests do + pass on SQLite only due to Python 3.11 issue with postgres bindings. +- Tested against Django 4.1. +- Drop Python 3.5 support. + 0.19.5 ------ 2022-11-20 diff --git a/Makefile b/Makefile index 72829e42f..3cb1fa165 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,9 @@ build-%: prepare-required-files stop: docker-compose -f docker-compose.yml stop; +touch: + docker-compose -f docker-compose.yml exec backend touch manage.py + make-migrations: docker-compose -f docker-compose.yml exec backend ./manage.py makemigrations $(APP); @@ -72,6 +75,12 @@ migrate: test: docker-compose -f docker-compose.yml exec backend pytest /backend/src/$(TEST_PATH); +tox-test: + docker-compose -f docker-compose.yml exec backend tox -e $(ARGS); + +tox-list: + docker-compose -f docker-compose.yml exec backend tox -l; + show-migrations: docker-compose -f docker-compose.yml exec backend ./manage.py showmigrations @@ -93,6 +102,33 @@ pip-install: pip-list: docker-compose -f docker-compose.yml exec backend pip list +pip-compile: + docker-compose -f docker-compose.yml exec backend pip install --upgrade pip && pip install pip-tools + docker-compose -f docker-compose.yml exec backend ls -al /backend/examples/requirements/ + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile captcha.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile common.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile debug.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile demo.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile deployment.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile dev.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile django_2_2.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile django_3_0.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile django_3_1.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile django_3_2.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile django_4_0.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile django_4_1.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile djangocms_3_4_3.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile djangorestframework.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile docs.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile feincms_1_17.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile feincms_1_20.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile latest.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile mptt.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile recaptcha.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile style_checkers.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile test.in + docker-compose -f docker-compose.yml exec -w /backend/examples/requirements/ backend pip-compile testing.in + black: docker-compose -f docker-compose.yml exec backend black . @@ -105,3 +141,8 @@ bash: prepare-required-files: mkdir -p examples/logs examples/db examples/media examples/media/static examples/media/fobi_plugins/content_image mkdir -p examples/media/fobi_plugins/file + +release: + python setup.py register + python setup.py sdist bdist_wheel + twine upload dist/* --verbose diff --git a/README.rst b/README.rst index 81a6b0a41..71df562c4 100644 --- a/README.rst +++ b/README.rst @@ -34,8 +34,8 @@ handling the submitted form data). Prerequisites ============= -- Django 2.2, 3.0, 3.1, 3.2 and 4.0. -- Python 3.6, 3.7, 3.8 and 3.9. +- Django 2.2, 3.0, 3.1, 3.2, 4.0 and 4.1. +- Python 3.6, 3.7, 3.8, 3.9, 3.10 and 3.11. Key concepts ============ diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index a3fdee607..5309557ff 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/python:3.9 +FROM docker.io/python:3.10-slim ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED 1 @@ -12,17 +12,8 @@ RUN apt-get update && \ nano \ chromium \ graphviz \ - libpq-dev - -RUN add-apt-repository ppa:deadsnakes/ppa --yes - -#RUN apt-get update && \ -# RUN apt-get install -y --no-install-recommends \ -# python3.6-dev \ -# python3.7-dev \ -# python3.8-dev - - #RUN apt-get install -y firefox + libpq-dev \ + python3.9 RUN pip install pip --upgrade RUN pip install virtualenv @@ -30,7 +21,7 @@ RUN pip install virtualenv RUN mkdir /backend WORKDIR /backend ADD examples/requirements/ /backend/requirements/ -RUN pip install -r /backend/requirements/django_3_2.in +RUN pip install -r /backend/requirements/django_4_1.in #RUN python -c "import geckodriver_autoinstaller; print(geckodriver_autoinstaller.install())" RUN python -c "from chromedriver_py import binary_path; print(binary_path)" COPY . /backend/ diff --git a/examples/requirements/captcha.txt b/examples/requirements/captcha.txt index 9d214b6a2..8f10cf4c6 100644 --- a/examples/requirements/captcha.txt +++ b/examples/requirements/captcha.txt @@ -18,5 +18,7 @@ pillow==8.2.0 # via django-simple-captcha pytz==2021.1 # via django +six==1.16.0 + # via django-simple-captcha sqlparse==0.4.1 # via django diff --git a/examples/requirements/common.in b/examples/requirements/common.in index 6f3db02e5..dc8cba40d 100644 --- a/examples/requirements/common.in +++ b/examples/requirements/common.in @@ -4,6 +4,7 @@ bleach>=2.1.2 decorator>=4.0.4 docopt>=0.4.0 docutils>=0.12 +importlib-metadata<5.0.0 Jinja2>=2.8 mailchimp>=2.0.9 markdown diff --git a/examples/requirements/demo.txt b/examples/requirements/demo.txt index adbfc9974..1ad0b0cd2 100644 --- a/examples/requirements/demo.txt +++ b/examples/requirements/demo.txt @@ -244,6 +244,7 @@ six==1.16.0 # via # -r common.in # bleach + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/requirements/dev.txt b/examples/requirements/dev.txt index efc77e363..d7eb08bd3 100644 --- a/examples/requirements/dev.txt +++ b/examples/requirements/dev.txt @@ -223,7 +223,7 @@ pluggy==0.13.1 # tox pre-commit==2.13.0 # via -r django_3_2.txt -psycopg2-binary==2.9.1 +psycopg2-binary==2.8.6 # via -r django_3_2.txt ptyprocess==0.7.0 # via @@ -300,6 +300,7 @@ six==1.16.0 # via # -r django_3_2.txt # bleach + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/requirements/django_2_2.txt b/examples/requirements/django_2_2.txt index d0968d835..e10fb9358 100644 --- a/examples/requirements/django_2_2.txt +++ b/examples/requirements/django_2_2.txt @@ -238,6 +238,7 @@ six==1.16.0 # via # -r common.in # bleach + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/requirements/django_3_0.txt b/examples/requirements/django_3_0.txt index 3541b836a..3318ba475 100644 --- a/examples/requirements/django_3_0.txt +++ b/examples/requirements/django_3_0.txt @@ -244,6 +244,7 @@ six==1.16.0 # via # -r common.in # bleach + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/requirements/django_3_1.txt b/examples/requirements/django_3_1.txt index fc4baf41c..092601a91 100644 --- a/examples/requirements/django_3_1.txt +++ b/examples/requirements/django_3_1.txt @@ -244,6 +244,7 @@ six==1.16.0 # via # -r common.in # bleach + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/requirements/django_3_2.txt b/examples/requirements/django_3_2.txt index 71b686b23..89f80d1b5 100644 --- a/examples/requirements/django_3_2.txt +++ b/examples/requirements/django_3_2.txt @@ -244,6 +244,7 @@ six==1.16.0 # via # -r common.in # bleach + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/requirements/django_4_1.in b/examples/requirements/django_4_1.in new file mode 100644 index 000000000..ac9a418aa --- /dev/null +++ b/examples/requirements/django_4_1.in @@ -0,0 +1,15 @@ +-r common.in +-r test.in +-r style_checkers.in +-r feincms_1_20.in + +Django>=4.1,<4.2 +django-admin-tools>=0.8.0 +django-autoslug>=1.9.6 +django-ckeditor>=5.8.0 +django-debug-toolbar>=2.1 +django-formtools>=2.2 +django-registration>=3.1.1 +django-simple-captcha>=0.5.12 +djangorestframework>=3.10 +easy-thumbnails>=2.7.0 diff --git a/examples/requirements/django_4_1.txt b/examples/requirements/django_4_1.txt new file mode 100644 index 000000000..572b2f003 --- /dev/null +++ b/examples/requirements/django_4_1.txt @@ -0,0 +1,349 @@ +# +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: +# +# pip-compile django_4_1.in +# +alabaster==0.7.12 + # via + # -r common.in + # sphinx +asgiref==3.5.2 + # via django +astroid==2.12.12 + # via pylint +async-generator==1.10 + # via + # trio + # trio-websocket +attrs==22.1.0 + # via + # outcome + # pytest + # trio +babel==2.11.0 + # via + # -r common.in + # sphinx +black==22.10.0 + # via -r style_checkers.in +bleach==5.0.1 + # via -r common.in +certifi==2022.9.24 + # via + # requests + # selenium +cfgv==3.3.1 + # via pre-commit +charset-normalizer==2.1.1 + # via requests +chromedriver-py==102.0.5005.61 + # via -r test.in +click==8.1.3 + # via black +confusable-homoglyphs==3.2.0 + # via django-registration +coverage[toml]==6.5.0 + # via + # -r test.in + # pytest-cov +decorator==5.1.1 + # via -r common.in +dill==0.3.6 + # via pylint +distlib==0.3.6 + # via virtualenv +django==4.1.3 + # via + # -r django_4_1.in + # django-ckeditor + # django-debug-toolbar + # django-formtools + # django-js-asset + # django-ranged-response + # django-registration + # django-simple-captcha + # djangorestframework + # easy-thumbnails + # feincms +django-admin-tools==0.9.2 + # via -r django_4_1.in +django-autoslug==1.9.8 + # via -r django_4_1.in +django-ckeditor==6.5.1 + # via -r django_4_1.in +django-debug-toolbar==3.7.0 + # via -r django_4_1.in +django-formtools==2.4 + # via -r django_4_1.in +django-js-asset==2.0.0 + # via + # django-ckeditor + # django-mptt +django-mptt==0.14.0 + # via + # -r feincms_1_20.in + # feincms +django-ranged-response==0.2.0 + # via django-simple-captcha +django-registration==3.3 + # via -r django_4_1.in +django-simple-captcha==0.5.17 + # via -r django_4_1.in +djangorestframework==3.14.0 + # via -r django_4_1.in +docopt==0.4.0 + # via + # -r common.in + # mailchimp +docutils==0.17.1 + # via + # -r common.in + # sphinx + # sphinx-rtd-theme +easy-thumbnails==2.8.3 + # via -r django_4_1.in +exceptiongroup==1.0.4 + # via trio +factory-boy==3.2.1 + # via -r test.in +faker==15.3.2 + # via + # -r test.in + # factory-boy +feincms==1.20.1 + # via -r feincms_1_20.in +filelock==3.8.0 + # via + # tox + # virtualenv +flake8==5.0.4 + # via -r style_checkers.in +h11==0.14.0 + # via wsproto +identify==2.5.8 + # via pre-commit +idna==3.4 + # via + # requests + # trio +imagesize==1.4.1 + # via sphinx +importlib-metadata==5.0.0 + # via + # markdown + # sphinx +iniconfig==1.1.1 + # via pytest +isort==5.10.1 + # via + # -r style_checkers.in + # pylint +jinja2==3.1.2 + # via + # -r common.in + # sphinx +lazy-object-proxy==1.8.0 + # via astroid +mailchimp==2.0.10 + # via -r common.in +markdown==3.4.1 + # via -r common.in +markupsafe==2.1.1 + # via + # -r common.in + # jinja2 +mccabe==0.7.0 + # via + # flake8 + # pylint +mypy-extensions==0.4.3 + # via black +nodeenv==1.7.0 + # via pre-commit +ordereddict==1.1 + # via -r common.in +outcome==1.2.0 + # via trio +packaging==21.3 + # via + # pytest + # sphinx + # tox +path==16.5.0 + # via path-py +path-py==12.5.0 + # via -r common.in +pathspec==0.10.2 + # via black +pexpect==4.8.0 + # via -r common.in +pickleshare==0.7.5 + # via -r common.in +pillow==9.3.0 + # via + # -r common.in + # django-simple-captcha + # easy-thumbnails + # feincms +platformdirs==2.5.4 + # via + # black + # pylint + # virtualenv +pluggy==1.0.0 + # via + # -r common.in + # pytest + # tox +pre-commit==2.20.0 + # via -r style_checkers.in +psycopg2-binary==2.8.6 + # via -r common.in +ptyprocess==0.7.0 + # via + # -r common.in + # pexpect +py==1.11.0 + # via + # -r test.in + # pytest + # tox +pycodestyle==2.9.1 + # via flake8 +pydocstyle==6.1.1 + # via -r style_checkers.in +pyflakes==2.5.0 + # via + # -r style_checkers.in + # flake8 +pygments==2.13.0 + # via + # -r common.in + # sphinx +pylint==2.15.5 + # via -r style_checkers.in +pyparsing==3.0.9 + # via packaging +pysocks==1.7.1 + # via urllib3 +pytest==6.2.5 + # via + # -r test.in + # pytest-cov + # pytest-django + # pytest-ordering + # pytest-pythonpath +pytest-cov==4.0.0 + # via -r test.in +pytest-django==4.5.2 + # via -r test.in +pytest-ordering==0.6 + # via -r test.in +pytest-pythonpath==0.7.4 + # via -r test.in +python-dateutil==2.8.2 + # via faker +pytz==2022.6 + # via + # -r common.in + # babel + # djangorestframework + # feincms +pyyaml==6.0 + # via pre-commit +requests==2.28.1 + # via + # mailchimp + # sphinx +selenium==4.6.0 + # via -r test.in +simplegeneric==0.8.1 + # via -r common.in +six==1.16.0 + # via + # -r common.in + # bleach + # feincms + # python-dateutil + # tox +sniffio==1.3.0 + # via trio +snowballstemmer==2.2.0 + # via + # -r common.in + # pydocstyle + # sphinx +sortedcontainers==2.4.0 + # via trio +sphinx==5.3.0 + # via + # -r common.in + # sphinx-rtd-theme +sphinx-rtd-theme==1.1.1 + # via -r common.in +sphinxcontrib-applehelp==1.0.2 + # via sphinx +sphinxcontrib-devhelp==1.0.2 + # via sphinx +sphinxcontrib-htmlhelp==2.0.0 + # via sphinx +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.3 + # via sphinx +sphinxcontrib-serializinghtml==1.1.5 + # via sphinx +sqlparse==0.4.3 + # via + # django + # django-debug-toolbar +toml==0.10.2 + # via + # pre-commit + # pytest +tomli==2.0.1 + # via + # black + # coverage + # pylint + # tox +tomlkit==0.11.6 + # via pylint +tox==3.27.1 + # via -r test.in +traitlets==5.5.0 + # via -r common.in +trio==0.22.0 + # via + # selenium + # trio-websocket +trio-websocket==0.9.2 + # via selenium +typing-extensions==4.4.0 + # via + # astroid + # black + # pylint +urllib3[socks]==1.26.12 + # via + # requests + # selenium +virtualenv==20.16.7 + # via + # -r common.in + # pre-commit + # tox +webencodings==0.5.1 + # via bleach +wheel==0.38.4 + # via -r common.in +wrapt==1.14.1 + # via astroid +wsproto==1.2.0 + # via trio-websocket +zipp==3.10.0 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/examples/requirements/docs.txt b/examples/requirements/docs.txt index f9fcfb104..ba690b35d 100644 --- a/examples/requirements/docs.txt +++ b/examples/requirements/docs.txt @@ -271,6 +271,7 @@ six==1.16.0 # -r common.in # bleach # django-fobi + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/requirements/test.in b/examples/requirements/test.in index 9b8757ea6..efbdda723 100644 --- a/examples/requirements/test.in +++ b/examples/requirements/test.in @@ -1,6 +1,7 @@ Faker coverage factory_boy +importlib-metadata<5.0.0 py pytest pytest-cov @@ -9,5 +10,5 @@ pytest-ordering pytest-pythonpath selenium tox -chromedriver-py==102.0.5005.61 +chromedriver-py==107.0.5304.62 #geckodriver-autoinstaller diff --git a/examples/requirements/testing.txt b/examples/requirements/testing.txt index e2a276af3..6bebd3324 100644 --- a/examples/requirements/testing.txt +++ b/examples/requirements/testing.txt @@ -244,6 +244,7 @@ six==1.16.0 # via # -r common.in # bleach + # django-simple-captcha # feincms # python-dateutil # tox diff --git a/examples/simple/foo/views.py b/examples/simple/foo/views.py index a17c04ebb..f5edb67a2 100644 --- a/examples/simple/foo/views.py +++ b/examples/simple/foo/views.py @@ -3,9 +3,7 @@ from django.http import HttpResponse from django.shortcuts import render -from django.template import RequestContext from django.views.decorators.csrf import csrf_exempt -from django_nine import versions from fobi.base import get_theme from fobi.helpers import handle_uploaded_file diff --git a/examples/simple/settings/base.py b/examples/simple/settings/base.py index 88c7d9ad9..cced8c6b2 100644 --- a/examples/simple/settings/base.py +++ b/examples/simple/settings/base.py @@ -1,7 +1,7 @@ # Django settings for example project. import os -from django_nine.versions import DJANGO_GTE_1_11, DJANGO_GTE_2_0, DJANGO_GTE_3_0 +from django_nine.versions import DJANGO_GTE_3_0 from selenium import webdriver @@ -699,10 +699,7 @@ def gettext(s): pass # Only now make proper assignments -if DJANGO_GTE_2_0: - MIDDLEWARE = _MIDDLEWARE -else: - MIDDLEWARE_CLASSES = _MIDDLEWARE +MIDDLEWARE = _MIDDLEWARE if DEBUG: try: diff --git a/examples/simple/urls/__init__.py b/examples/simple/urls/__init__.py new file mode 100644 index 000000000..3bb8cb099 --- /dev/null +++ b/examples/simple/urls/__init__.py @@ -0,0 +1 @@ +from .class_based import * diff --git a/examples/simple/urls.py b/examples/simple/urls/class_based.py similarity index 95% rename from examples/simple/urls.py rename to examples/simple/urls/class_based.py index 8ab14a7df..f45fae36f 100644 --- a/examples/simple/urls.py +++ b/examples/simple/urls/class_based.py @@ -10,6 +10,10 @@ from fobi.settings import DEFAULT_THEME +__all__ = ( + "urlpatterns", +) + admin.autodiscover() # Mapping. @@ -60,14 +64,9 @@ ), ] -if versions.DJANGO_GTE_2_0: - url_patterns_args += [ - url(r"^admin/", admin.site.urls), - ] -else: - url_patterns_args += [ - url(r"^admin/", include(admin.site.urls)), - ] +url_patterns_args += [ + url(r"^admin/", admin.site.urls), +] url_patterns_args += [ # django-registration URLs: diff --git a/examples/simple/urls/function_based.py b/examples/simple/urls/function_based.py new file mode 100644 index 000000000..198d03cc6 --- /dev/null +++ b/examples/simple/urls/function_based.py @@ -0,0 +1,158 @@ +from django.conf import settings +from django.conf.urls.i18n import i18n_patterns +from django.conf.urls.static import static +from django.contrib import admin +from django.contrib.auth import views as auth_views +from django.contrib.staticfiles.urls import staticfiles_urlpatterns +from django.urls import include, re_path as url +from django.views.generic import TemplateView +from django_nine import versions + +from fobi.settings import DEFAULT_THEME + +__all__ = ( + "urlpatterns", +) + +admin.autodiscover() + +# Mapping. +fobi_theme_home_template_mapping = { + "bootstrap3": "home/bootstrap3.html", + "foundation5": "home/foundation5.html", + "simple": "home/simple.html", +} + +# Get the template to be used. +fobi_home_template = fobi_theme_home_template_mapping.get( + DEFAULT_THEME, "home/base.html" +) + +FOBI_EDIT_URLS_PREFIX = "" +if DEFAULT_THEME in ("simple", "djangocms_admin_style_theme"): + FOBI_EDIT_URLS_PREFIX = "admin/" + +urlpatterns = [] + +url_patterns_args = [ + # DB Store plugin URLs + # namespace='fobi' + url( + r"^fobi/plugins/form-handlers/db-store/", + include("fobi.contrib.plugins.form_handlers.db_store.urls"), + ), + url( + r"^fobi/plugins/form-wizard-handlers/db-store/", + include( + "fobi.contrib.plugins.form_handlers.db_store.urls." + "form_wizard_handlers" + ), + ), + # django-fobi URLs: + # namespace='fobi' + url(r"^fobi/", include("fobi.urls.view")), + # namespace='fobi' + url( + r"^{0}fobi/".format(FOBI_EDIT_URLS_PREFIX), + include("fobi.urls.edit"), + ), + url(r"^admin_tools/", include("admin_tools.urls")), + url( + r"^login/$", + auth_views.LoginView.as_view(template_name="registration/login.html"), + name="auth_login", + ), +] + +url_patterns_args += [ + url(r"^admin/", admin.site.urls), +] + +url_patterns_args += [ + # django-registration URLs: + url( + r"^accounts/", + include( + "django_registration.backends.one_step.urls" + if versions.DJANGO_GTE_3_0 + else "registration.backends.simple.urls" + ), + ), + # foo URLs: + url(r"^foo/", include("foo.urls")), + # bar URLs: + # url(r'^bar/', include('bar.urls')), + url(r"^$", TemplateView.as_view(template_name=fobi_home_template)), + # django-fobi public forms contrib app: + # url(r'^', include('fobi.contrib.apps.public_forms.urls')), +] + +urlpatterns += i18n_patterns(*url_patterns_args) + +# Serving media and static in debug/developer mode. +if settings.DEBUG: + urlpatterns += staticfiles_urlpatterns() + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + +# Conditionally including FeinCMS URls in case if +# FeinCMS in installed apps. +if "feincms" in settings.INSTALLED_APPS: + from page.models import Page + + Page + url_patterns_args = [ + url(r"^pages/", include("feincms.urls")), + ] + urlpatterns += i18n_patterns(*url_patterns_args) + +# # Conditionally include django-markdownx +# if 'markdownx' in settings.INSTALLED_APPS: +# url_patterns_args = [ +# url(r'^markdownx/', include('markdownx.urls')), +# ] +# urlpatterns += list(url_patterns_args) + +if "ckeditor_uploader" in settings.INSTALLED_APPS: + url_patterns_args = [ + url(r"^ckeditor/", include("ckeditor_uploader.urls")), + ] + urlpatterns += i18n_patterns(*url_patterns_args) + +# Conditionally including DjangoCMS URls in case if +# DjangoCMS in installed apps. +if "cms" in settings.INSTALLED_APPS: + url_patterns_args = [ + url(r"^cms-pages/", include("cms.urls")), + ] + urlpatterns += i18n_patterns(*url_patterns_args) + +# Conditionally including Django REST framework integration app +if "fobi.contrib.apps.drf_integration" in settings.INSTALLED_APPS: + from fobi.contrib.apps.drf_integration.urls import fobi_router + + urlpatterns += [url(r"^api/", include(fobi_router.urls))] + +# Conditionally including Captcha URls in case if +# Captcha in installed apps. +if getattr(settings, "ENABLE_CAPTCHA", False): + try: + from captcha.fields import ReCaptchaField + except ImportError: + try: + from captcha.fields import CaptchaField + + if "captcha" in settings.INSTALLED_APPS: + urlpatterns += [ + url(r"^captcha/", include("captcha.urls")), + ] + except ImportError: + pass + +if getattr(settings, "DEBUG", False) and getattr( + settings, "DEBUG_TOOLBAR", False +): + import debug_toolbar + + urlpatterns = [ + url(r"^__debug__/", include(debug_toolbar.urls)), + ] + urlpatterns diff --git a/scripts/compile_requirements.sh b/scripts/compile_requirements.sh index af02ffa08..f559761d9 100755 --- a/scripts/compile_requirements.sh +++ b/scripts/compile_requirements.sh @@ -11,6 +11,7 @@ pip-compile django_3_0.in "$@" pip-compile django_3_1.in "$@" pip-compile django_3_2.in "$@" pip-compile django_4_0.in "$@" +pip-compile django_4_1.in "$@" pip-compile djangocms_3_4_3.in "$@" pip-compile djangorestframework.in "$@" pip-compile docs.in "$@" diff --git a/setup.py b/setup.py index 2cc230f02..c24a0150c 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from distutils.version import LooseVersion from setuptools import setup, find_packages -version = "0.19.5" +version = "0.19.6" # *************************************************************************** # ************************** Django version ********************************* @@ -96,7 +96,7 @@ ".. figure:: https://github.com/barseghyanartur/django-fobi/raw/" "main/docs/_static", ) -except: +except OSError: readme = "" screenshots = "" @@ -251,6 +251,7 @@ "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Environment :: Web Environment", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "License :: OSI Approved :: GNU Lesser General Public License v2 or " diff --git a/src/fobi/__init__.py b/src/fobi/__init__.py index 3d2a2fc34..52b3e3653 100644 --- a/src/fobi/__init__.py +++ b/src/fobi/__init__.py @@ -1,5 +1,5 @@ __title__ = "django-fobi" -__version__ = "0.19.5" +__version__ = "0.19.6" __author__ = "Artur Barseghyan " __copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" diff --git a/src/fobi/admin.py b/src/fobi/admin.py index 240d46529..5b371fea0 100644 --- a/src/fobi/admin.py +++ b/src/fobi/admin.py @@ -2,12 +2,10 @@ from django.contrib.admin import helpers from django.contrib.admin.views.decorators import staff_member_required from django.shortcuts import redirect, render -from django.template import RequestContext from django.urls import re_path as url from django.utils.decorators import method_decorator from django.utils.html import strip_tags from django.utils.translation import gettext_lazy as _ -from django_nine import versions from .constants import ACTION_CHOICE_REPLACE from .forms import ( @@ -71,7 +69,7 @@ def base_bulk_change_plugins( opts = modeladmin.model._meta app_label = opts.app_label - selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME) + selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME) post = dict(request.POST) if selected: post["selected_plugins"] = ",".join(selected) @@ -577,7 +575,9 @@ def bulk_change_plugins(self, request): class FormElementAdmin(BasePluginModelAdmin): """FormElement admin.""" - actions = [bulk_change_form_element_plugins] + BasePluginModelAdmin.actions + actions = (bulk_change_form_element_plugins,) + tuple( + BasePluginModelAdmin.actions + ) def _get_bulk_change_form_class(self): """Get bulk change form class.""" @@ -614,7 +614,9 @@ def get_urls(self): class FormHandlerAdmin(BasePluginModelAdmin): """FormHandler admin.""" - actions = [bulk_change_form_handler_plugins] + BasePluginModelAdmin.actions + actions = (bulk_change_form_handler_plugins,) + tuple( + BasePluginModelAdmin.actions + ) def _get_bulk_change_form_class(self): """Get bulk change form class.""" @@ -651,9 +653,9 @@ def get_urls(self): class FormWizardHandlerAdmin(BasePluginModelAdmin): """FormHandler admin.""" - actions = [ - bulk_change_form_wizard_handler_plugins - ] + BasePluginModelAdmin.actions + actions = (bulk_change_form_wizard_handler_plugins,) + tuple( + BasePluginModelAdmin.actions + ) def _get_bulk_change_form_class(self): """Get bulk change form class.""" diff --git a/src/fobi/base.py b/src/fobi/base.py index 2b70a9585..38edb6c81 100644 --- a/src/fobi/base.py +++ b/src/fobi/base.py @@ -61,7 +61,7 @@ __title__ = "fobi.base" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ( "assemble_form_field_widget_class", diff --git a/src/fobi/contrib/apps/drf_integration/dynamic.py b/src/fobi/contrib/apps/drf_integration/dynamic.py index 3b0d72360..051928e52 100644 --- a/src/fobi/contrib/apps/drf_integration/dynamic.py +++ b/src/fobi/contrib/apps/drf_integration/dynamic.py @@ -1,7 +1,8 @@ from __future__ import unicode_literals import copy -from collections import Mapping, OrderedDict +from collections import OrderedDict +from collections.abc import Mapping import six from django.core.exceptions import ValidationError as DjangoValidationError @@ -24,7 +25,7 @@ __title__ = "fobi.contrib.apps.drf_integration.dynamic" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ( "assemble_serializer_class", diff --git a/src/fobi/contrib/apps/drf_integration/form_elements/fields/null_boolean/base.py b/src/fobi/contrib/apps/drf_integration/form_elements/fields/null_boolean/base.py index 6fd3cc2c4..88e8952e5 100644 --- a/src/fobi/contrib/apps/drf_integration/form_elements/fields/null_boolean/base.py +++ b/src/fobi/contrib/apps/drf_integration/form_elements/fields/null_boolean/base.py @@ -1,5 +1,5 @@ from django.utils.translation import gettext_lazy as _ -from rest_framework.fields import NullBooleanField +from rest_framework.fields import BooleanField from .......base import IntegrationFormFieldPlugin from .... import UID as INTEGRATE_WITH_UID @@ -43,9 +43,10 @@ def get_custom_field_instances( "initial": form_element_plugin.data.initial, "label": form_element_plugin.data.label, "help_text": form_element_plugin.data.help_text, + "allow_null": True, } return [ DRFIntegrationFormElementPluginProcessor( - field_class=NullBooleanField, field_kwargs=field_kwargs + field_class=BooleanField, field_kwargs=field_kwargs ) ] diff --git a/src/fobi/contrib/apps/drf_integration/serializers.py b/src/fobi/contrib/apps/drf_integration/serializers.py index 217a47e68..73523ad62 100644 --- a/src/fobi/contrib/apps/drf_integration/serializers.py +++ b/src/fobi/contrib/apps/drf_integration/serializers.py @@ -1,4 +1,3 @@ -# from __future__ import unicode_literals from collections import OrderedDict from rest_framework import serializers @@ -8,7 +7,7 @@ __title__ = "fobi.contrib.apps.drf_integration.serializers" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("FormEntrySerializer",) diff --git a/src/fobi/contrib/apps/drf_integration/views.py b/src/fobi/contrib/apps/drf_integration/views.py index 671f016f3..2266a2a51 100644 --- a/src/fobi/contrib/apps/drf_integration/views.py +++ b/src/fobi/contrib/apps/drf_integration/views.py @@ -1,8 +1,6 @@ -# from __future__ import unicode_literals from django.contrib import messages from django.http import HttpRequest from django.utils.translation import gettext -from django_nine import versions from rest_framework import mixins, permissions from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet diff --git a/src/fobi/contrib/apps/mezzanine_integration/page_processors.py b/src/fobi/contrib/apps/mezzanine_integration/page_processors.py index 01df65c45..fa77f0b1c 100644 --- a/src/fobi/contrib/apps/mezzanine_integration/page_processors.py +++ b/src/fobi/contrib/apps/mezzanine_integration/page_processors.py @@ -1,4 +1,3 @@ -# from mezzanine.conf import settings from mezzanine.pages.page_processors import processor_for from .models import FobiFormPage diff --git a/src/fobi/contrib/plugins/form_elements/content/content_image/base.py b/src/fobi/contrib/plugins/form_elements/content/content_image/base.py index 1babddddc..cc0b3adcc 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_image/base.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_image/base.py @@ -22,7 +22,7 @@ __title__ = "fobi.contrib.plugins.form_elements.content.content_image.base" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("ContentImagePlugin",) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_image_url/base.py b/src/fobi/contrib/plugins/form_elements/content/content_image_url/base.py index 8869ec7d5..651c54af6 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_image_url/base.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_image_url/base.py @@ -15,7 +15,7 @@ __title__ = "fobi.contrib.plugins.form_elements.content.content_image_url.base" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("ContentImageURLPlugin",) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_markdown/base.py b/src/fobi/contrib/plugins/form_elements/content/content_markdown/base.py index d6e8b4280..9702c626e 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_markdown/base.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_markdown/base.py @@ -14,7 +14,7 @@ __title__ = "fobi.contrib.plugins.form_elements.content.content_richtext.base" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("ContentMarkdownPlugin",) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/base.py b/src/fobi/contrib/plugins/form_elements/content/content_text/base.py index 33860b09e..f85fc926c 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_text/base.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/base.py @@ -14,7 +14,7 @@ __title__ = "fobi.contrib.plugins.form_elements.content.content_text.base" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("ContentTextPlugin",) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_video/base.py b/src/fobi/contrib/plugins/form_elements/content/content_video/base.py index b34579090..6957f604c 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_video/base.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_video/base.py @@ -14,7 +14,7 @@ __title__ = "fobi.contrib.plugins.form_elements.content.content_video.base" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("ContentVideoPlugin",) diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/base.py b/src/fobi/contrib/plugins/form_elements/fields/slider/base.py index 581af44e8..4baf8efa0 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/base.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/base.py @@ -5,7 +5,6 @@ from django.utils.html import format_html from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -from django_nine import versions from six import PY3, text_type from . import UID diff --git a/src/fobi/contrib/plugins/form_handlers/db_store/views.py b/src/fobi/contrib/plugins/form_handlers/db_store/views.py index 6eddf9246..245e29897 100644 --- a/src/fobi/contrib/plugins/form_handlers/db_store/views.py +++ b/src/fobi/contrib/plugins/form_handlers/db_store/views.py @@ -1,9 +1,6 @@ from django.contrib.auth.decorators import login_required from django.shortcuts import render -from django.template import RequestContext -from django_nine import versions -# from fobi.decorators import permissions_required, SATISFY_ALL, SATISFY_ANY from .....base import ( get_form_handler_plugin_widget, get_form_wizard_handler_plugin_widget, diff --git a/src/fobi/dynamic.py b/src/fobi/dynamic.py index 0f7249e70..ab35953d7 100644 --- a/src/fobi/dynamic.py +++ b/src/fobi/dynamic.py @@ -15,7 +15,7 @@ __title__ = "fobi.dynamic" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ( "assemble_form_class", diff --git a/src/fobi/migrations/0010_formwizardhandler.py b/src/fobi/migrations/0010_formwizardhandler.py index a1b4542be..aab2e153d 100644 --- a/src/fobi/migrations/0010_formwizardhandler.py +++ b/src/fobi/migrations/0010_formwizardhandler.py @@ -5,7 +5,6 @@ from django.conf import settings from django.db import migrations, models -from django_nine import versions class Migration(migrations.Migration): diff --git a/src/fobi/tests/__init__.py b/src/fobi/tests/__init__.py index 470ab2888..e69de29bb 100644 --- a/src/fobi/tests/__init__.py +++ b/src/fobi/tests/__init__.py @@ -1,17 +0,0 @@ -from __future__ import print_function - -import unittest - -from fobi.tests.test_browser_build_dynamic_forms import * - -# from fobi.tests.test_core import * -# from fobi.tests.test_dynamic_forms import * -# from fobi.tests.test_sortable_dict import * - -__title__ = "fobi.tests" -__author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" -__license__ = "GPL 2.0/LGPL 2.1" - -if __name__ == "__main__": - unittest.main() diff --git a/src/fobi/tests/base.py b/src/fobi/tests/base.py index 9c1af54d5..229d3a291 100644 --- a/src/fobi/tests/base.py +++ b/src/fobi/tests/base.py @@ -7,6 +7,7 @@ from django.core.management import call_command from django.urls import reverse from selenium import webdriver +from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.firefox.firefox_binary import FirefoxBinary @@ -143,16 +144,17 @@ def _authenticate(self): "{0}{1}".format(self._get_live_server_url(), reverse("auth_login")) ) self._maximize_window() - username_input = self.driver.find_element_by_name("username") + username_input = self.driver.find_element(By.NAME, "username") username_input.send_keys(constants.FOBI_TEST_USER_USERNAME) - password_input = self.driver.find_element_by_name("password") + password_input = self.driver.find_element(By.NAME, "password") password_input.send_keys(constants.FOBI_TEST_USER_PASSWORD) - self.driver.find_element_by_xpath('//button[@type="submit"]').click() + self.driver.find_element(By.XPATH, '//button[@type="submit"]').click() # Wait until the list view opens WebDriverWait(self.driver, timeout=TIMEOUT).until( - # lambda driver: driver.find_element_by_id('id_main') - lambda driver: driver.find_element_by_xpath( + # lambda driver: driver.find_element(By.ID, 'id_main') + lambda driver: driver.find_element( + By.XPATH, '//body[contains(@class, "theme")]' ) ) @@ -196,12 +198,12 @@ def _scroll_by(self, x, y): def _scroll_page_top(self): """Scroll to the page top.""" - html = self.driver.find_element_by_tag_name("html") + html = self.driver.find_element(By.TAG_NAME, "html") html.send_keys(Keys.HOME) def _scroll_page_bottom(self): """Scroll to the page bottom.""" - html = self.driver.find_element_by_tag_name("html") + html = self.driver.find_element(By.TAG_NAME, "html") html.send_keys(Keys.END) def take_screenshot(self, name="screenshot"): diff --git a/src/fobi/tests/data.py b/src/fobi/tests/data.py index 51d63a4b5..f731814d3 100644 --- a/src/fobi/tests/data.py +++ b/src/fobi/tests/data.py @@ -99,7 +99,7 @@ __title__ = "fobi.tests.data" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ( "TEST_DYNAMIC_FORMS_DEFINITION_DATA", @@ -252,10 +252,13 @@ # }, } +# Note, that value of the test_date_input might be the source of failing +# tests. ATM, system preferences are set to US format (mm/dd/YYYY). That's +# why we need to format the value here accordingly. TEST_FORM_FIELD_DATA = { "test_boolean": True, # 'test_checkbox_select_multiple_input': '', - "test_date_input": datetime.date.today().strftime("%d-%m-%Y"), + "test_date_input": datetime.date.today().strftime("%m-%d-%Y"), "test_datetime_input": datetime.datetime.now().strftime( "%Y-%m-%d %H:%M:%S" ), @@ -313,7 +316,7 @@ ), ( force_str(HTTPRepostHandlerPlugin.name), - {"endpoint_url": "http://dev.example.com"}, + {"endpoint_url": "https://webhook.site/a91e7062-83c4"}, ), ] ) diff --git a/src/fobi/tests/test_browser_build_dynamic_forms.py b/src/fobi/tests/test_browser_build_dynamic_forms.py index 38bcdd081..b33ab24d7 100644 --- a/src/fobi/tests/test_browser_build_dynamic_forms.py +++ b/src/fobi/tests/test_browser_build_dynamic_forms.py @@ -1,14 +1,12 @@ import logging -import os -import unittest -from datetime import datetime -from django.conf import settings from django.urls import reverse -from selenium.webdriver.common.action_chains import ActionChains -# from selenium.webdriver.common.keys import Keys +from django.test import override_settings +from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait +from fobi.models import FormEntry + from . import constants from .base import BaseFobiBrowserBuldDynamicFormsTest from .core import print_info @@ -19,11 +17,9 @@ ) from .helpers import db_clean_up -from fobi.models import FormEntry - __title__ = "fobi.tests.test_browser_build_dynamic_forms" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("FobiBrowserBuldDynamicFormsTest",) @@ -57,7 +53,8 @@ def _go_to_dashboard(self, wait=WAIT_FOR): # Wait until the edit widget form opens WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, '//body[contains(@class, "theme-bootstrap3")]' ) ) @@ -71,14 +68,16 @@ def _test_add_form(self, wait=WAIT_FOR): # Follow the create form link. # Click the button to go to dashboard edit - self.driver.find_element_by_xpath( + self.driver.find_element( + By.XPATH, '//a[contains(@class, "list-group-item")]' ).click() # Wait until the dashboard edit view opens WebDriverWait(self.driver, timeout=TIMEOUT).until( - # lambda driver: driver.find_element_by_id('id_main') - lambda driver: driver.find_element_by_xpath( + # lambda driver: driver.find_element(By.ID, 'id_main') + lambda driver: driver.find_element( + By.XPATH, '//body[contains(@class, "theme-bootstrap3")]' ) ) @@ -94,11 +93,11 @@ def _test_add_form(self, wait=WAIT_FOR): if form_data: for field_name, field_value in form_data.items(): - field_input = self.driver.find_element_by_name(field_name) + field_input = self.driver.find_element(By.NAME, field_name) field_input.send_keys(field_value) # Click add widget button - self.driver.find_element_by_xpath('//button[@type="submit"]').click() + self.driver.find_element(By.XPATH, '//button[@type="submit"]').click() logger.debug( """//div[contains(text(), 'Form {0} was created """ @@ -109,7 +108,8 @@ def _test_add_form(self, wait=WAIT_FOR): # Wait until the fobi page opens with the form element in WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//div[contains(text(), 'Form {0} was created """ """successfully.') """ """and contains(@class, "alert-info")]""".format( @@ -138,14 +138,16 @@ def _add_form_element( """ # # Wait until the add widget view opens # WebDriverWait(self.driver, timeout=TIMEOUT).until( - # lambda driver: driver.find_element_by_xpath( + # lambda driver: driver.find_element( + # By.XPATH, # """//a[contains(text(), 'Choose form element to add') and """ # """contains(@class, "dropdown-toggle")]""" # ) # ) # try: - # add_form_element_link = self.driver.find_element_by_xpath( + # add_form_element_link = self.driver.find_element( + # By.XPATH, # """//a[contains(text(), 'Choose form element to add') and """ # """contains(@class, "dropdown-toggle")]""" # ) @@ -154,7 +156,8 @@ def _add_form_element( # Click the add form element button to add a new form element to the # form. - add_form_element_link = self.driver.find_element_by_xpath( + add_form_element_link = self.driver.find_element( + By.XPATH, """//a[contains(text(), 'Choose form element to add') and """ """contains(@class, "dropdown-toggle")]""" ) @@ -164,19 +167,21 @@ def _add_form_element( # Find the parent element add_form_element_parent_container = ( - add_form_element_link.find_element_by_xpath("..") + add_form_element_link.find_element(By.XPATH, "..") ) # Find the container of the available form elements add_form_element_available_elements_container = ( - add_form_element_parent_container.find_element_by_xpath( + add_form_element_parent_container.find_element( + By.XPATH, '//ul[contains(@class, "dropdown-menu")]' ) ) # Click on the element we want form_element_to_add = ( - add_form_element_available_elements_container.find_element_by_xpath( + add_form_element_available_elements_container.find_element( + By.XPATH, '//a[text()="{0}"]'.format(form_element_name) ) ) @@ -196,7 +201,8 @@ def _add_form_element( if form_element_data: # Wait until the add widget view opens WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//h1[contains(text(), 'Add "{0}" element to """ """the form')]""".format(form_element_name) ) @@ -205,14 +211,15 @@ def _add_form_element( for field_name, field_value in form_element_data.items(): # Wait until element is visible WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_name(field_name) + lambda driver: driver.find_element(By.NAME, field_name) ) - field_input = self.driver.find_element_by_name(field_name) + field_input = self.driver.find_element(By.NAME, field_name) # field_input.clear() field_input.send_keys(field_value) # Click add widget button - submit_button = self.driver.find_element_by_xpath( + submit_button = self.driver.find_element( + By.XPATH, '//button[@type="submit"]' ) @@ -229,7 +236,8 @@ def _add_form_element( # Wait until the fobi page opens with the form element in. self._maximize_window() WebDriverWait(self.driver, timeout=LONG_TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//div[contains(text(), 'The form element plugin "{0}" """ """was added successfully.') """ """and contains(@class, "alert-info")]""".format( @@ -280,7 +288,8 @@ def _remove_form_element( """ # Get the label of the given form element in order to delete it later # from the form. - delete_form_element_label = self.driver.find_element_by_xpath( + delete_form_element_label = self.driver.find_element( + By.XPATH, """//label[contains(text(), '({0})') """ """and contains(@class, "control-label")]""".format( form_element_name @@ -289,12 +298,13 @@ def _remove_form_element( # Get the parent of the label delete_form_element_label_parent_container = ( - delete_form_element_label.find_element_by_xpath("..") + delete_form_element_label.find_element(By.XPATH, "..") ) # Click the add form element button to add a new form element to the # form. - delete_form_element_link = delete_form_element_label_parent_container.find_element_by_partial_link_text( + delete_form_element_link = delete_form_element_label_parent_container.find_element( + By.PARTIAL_LINK_TEXT, "Delete" ) # delete_form_element_link.click() @@ -305,7 +315,8 @@ def _remove_form_element( # Wait until the fobi page opens with the form element in. WebDriverWait(self.driver, timeout=LONG_TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//div[contains(text(), 'The form element plugin "{0}" """ """was deleted successfully.') """ """and contains(@class, "alert-info")]""".format( @@ -338,14 +349,16 @@ def _add_form_handler( # tab with form handlers. Otherwise Selenium raises # an exception about non-visible element on the page # that we're trying to fetch. - form_handlers_tab_link = self.driver.find_element_by_xpath( + form_handlers_tab_link = self.driver.find_element( + By.XPATH, """//a[@href="#tab-form-handlers"]""" ) form_handlers_tab_link.click() # Click the add form element button to add a new form element to the # form. - add_form_handler_link = self.driver.find_element_by_xpath( + add_form_handler_link = self.driver.find_element( + By.XPATH, """//a[contains(text(), 'Choose form handler to add') """ """and contains(@class, "dropdown-toggle")]""" ) @@ -353,19 +366,21 @@ def _add_form_handler( # Find the parent element add_form_handler_parent_container = ( - add_form_handler_link.find_element_by_xpath("..") + add_form_handler_link.find_element(By.XPATH, "..") ) # Find the container of the available form elements add_form_handler_available_elements_container = ( - add_form_handler_parent_container.find_element_by_xpath( + add_form_handler_parent_container.find_element( + By.XPATH, '//ul[contains(@class, "dropdown-menu")]' ) ) # Click on the element we want form_handler_to_add = ( - add_form_handler_available_elements_container.find_element_by_xpath( + add_form_handler_available_elements_container.find_element( + By.XPATH, '//a[text()="{0}"]'.format(form_handler_name) ) ) @@ -375,7 +390,8 @@ def _add_form_handler( if form_handler_data: # Wait until the add widget view opens WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//h1[contains(text(), 'Add "{0}" handler to """ """the form')]""".format(form_handler_name) ) @@ -383,17 +399,19 @@ def _add_form_handler( # Config for field_name, field_value in form_handler_data.items(): - field_input = self.driver.find_element_by_name(field_name) + field_input = self.driver.find_element(By.NAME, field_name) field_input.send_keys(field_value) # Click add widget button - self.driver.find_element_by_xpath( + self.driver.find_element( + By.XPATH, '//button[@type="submit"]' ).click() # Wait until the fobi page opens with the form element in. WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//div[contains(text(), 'The form handler plugin "{0}" """ """was added successfully.') """ """and contains(@class, "alert-info")]""".format( @@ -439,25 +457,28 @@ def _remove_form_handler( # tab with form handlers. Otherwise Selenium raises # an exception about non-visible element on the page # that we're trying to fetch. - form_handlers_tab_link = self.driver.find_element_by_xpath( + form_handlers_tab_link = self.driver.find_element( + By.XPATH, """//a[@href="#tab-form-handlers"]""" ) form_handlers_tab_link.click() # Get the label of the given form element in order to delete it later # from the form. - delete_form_handler_label = self.driver.find_element_by_xpath( + delete_form_handler_label = self.driver.find_element( + By.XPATH, """//td[contains(text(), '{0}')]""".format(form_handler_name) ) # Get the parent of the label delete_form_handler_label_parent_container = ( - delete_form_handler_label.find_element_by_xpath("..") + delete_form_handler_label.find_element(By.XPATH, "..") ) # Click the add form element button to add a new form element to the # form. - delete_form_handler_link = delete_form_handler_label_parent_container.find_element_by_partial_link_text( + delete_form_handler_link = delete_form_handler_label_parent_container.find_element( + By.PARTIAL_LINK_TEXT, "Delete" ) delete_form_handler_link.click() @@ -466,7 +487,8 @@ def _remove_form_handler( # Wait until the fobi page opens with the form element in. WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//div[contains(text(), 'The form handler plugin "{0}" """ """was deleted successfully.') """ """and contains(@class, "alert-info")]""".format( @@ -496,10 +518,20 @@ def _test_remove_form_handlers(self): # +++++++++++++++++++++++++++ General +++++++++++++++++++++++++++ # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + def _test_1001_open_dashboard(self): + """Test open dashboard.""" + self._go_to_dashboard() + @print_info def test_1001_open_dashboard(self): """Test open dashboard.""" - self._go_to_dashboard() + self._test_1001_open_dashboard() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_1001_open_dashboard_fbv(self): + """Test open dashboard.""" + self._test_1001_open_dashboard() # class GeneralFobiBrowserBuldDynamicFormsTest( # BaseFobiBrowserBuldDynamicFormsTest): @@ -514,8 +546,7 @@ def test_1001_open_dashboard(self): # ++++++++++++++++++++++ Form specific ++++++++++++++++++++++++++ # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @print_info - def test_2001_add_form(self): + def _test_2001_add_form(self): """Test add a new form.""" # Clean up database db_clean_up() @@ -523,24 +554,55 @@ def test_2001_add_form(self): self._test_add_form(wait=WAIT_FOR) # Make sure the success message is there - # self.driver.find_element_by_xpath( + # self.driver.find_element( + # By.XPATH, # """//div[text()='Form {0} was created successfully.']""".format( # constants.TEST_FORM_NAME # ) # ) + @print_info + def test_2001_add_form(self): + """Test add a new form.""" + self._test_2001_add_form() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_2001_add_form_fbv(self): + """Test add a new form.""" + self._test_2001_add_form() + + def _test_2002_edit_form(self): + """Test edit a form.""" + # TODO + @print_info def test_2002_edit_form(self): """Test edit a form.""" + self._test_2002_edit_form() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_2002_edit_form_fbv(self): + """Test edit a form.""" + self._test_2002_edit_form() + + def _test_2003_delete_form(self): + """Test delete a form.""" # TODO @print_info def test_2003_delete_form(self): """Test delete a form.""" - # TODO + self._test_2003_delete_form() + @override_settings(ROOT_URLCONF="urls.function_based") @print_info - def test_2004_submit_form(self, wait=WAIT_FOR): + def test_2003_delete_form_fbv(self): + """Test delete a form.""" + self._test_2003_delete_form() + + def _test_2004_submit_form(self, wait=WAIT_FOR): """Test submit form.""" # Clean up database db_clean_up() @@ -560,31 +622,34 @@ def test_2004_submit_form(self, wait=WAIT_FOR): # Wait until the edit widget form opens WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, '//body[contains(@class, "theme-bootstrap3")]' ) ) for field_name, field_value in TEST_FORM_FIELD_DATA.items(): - field_input = self.driver.find_element_by_name(field_name) + field_input = self.driver.find_element(By.NAME, field_name) field_input.send_keys(field_value) self.take_screenshot("filled_form_page") self._sleep(2) - footer = self.driver.find_element_by_xpath("//footer") + footer = self.driver.find_element(By.XPATH, "//footer") footer.click() self._scroll_page_bottom() # Wait until button is there WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, '//button[@type="submit"]' ) ) # Click add widget button - submit_button = self.driver.find_element_by_xpath( + submit_button = self.driver.find_element( + By.XPATH, '//button[@type="submit"]' ) @@ -606,7 +671,8 @@ def test_2004_submit_form(self, wait=WAIT_FOR): # twice as long as the normal timeout. self.take_screenshot("submit_success_page") WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, """//div[contains(text(), 'Form {0} was submitted """ """successfully.') """ """and contains(@class, "alert-info")]""".format( @@ -621,12 +687,28 @@ def test_2004_submit_form(self, wait=WAIT_FOR): # BaseFobiBrowserBuldDynamicFormsTest): # """Form element specific.""" + @print_info + @override_settings( + EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend" + ) + def test_2004_submit_form(self, wait=WAIT_FOR): + """Test submit form.""" + self._test_2004_submit_form() + + @print_info + @override_settings( + EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend", + ROOT_URLCONF="urls.function_based", + ) + def test_2004_submit_form_fbv(self, wait=WAIT_FOR): + """Test submit form.""" + self._test_2004_submit_form() + # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # ++++++++++++++++++++ Form element specific ++++++++++++++++++++ # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @print_info - def test_3001_add_form_elements(self, wait=WAIT_FOR): + def _test_3001_add_form_elements(self, wait=WAIT_FOR): """Test adding form elements.""" db_clean_up() # Clean up database @@ -635,7 +717,17 @@ def test_3001_add_form_elements(self, wait=WAIT_FOR): self._sleep(wait) @print_info - def test_3002_remove_form_elements(self): + def test_3001_add_form_elements(self, wait=WAIT_FOR): + """Test adding form elements.""" + self._test_3001_add_form_elements() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_3001_add_form_elements_fbv(self, wait=WAIT_FOR): + """Test adding form elements.""" + self._test_3001_add_form_elements() + + def _test_3002_remove_form_elements(self): """Test remove form element.""" # Clean up database db_clean_up() @@ -645,10 +737,31 @@ def test_3002_remove_form_elements(self): self._test_remove_form_elements() @print_info - def test_3003_edit_form_elements(self): + def test_3002_remove_form_elements(self): + """Test remove form element.""" + self._test_3002_remove_form_elements() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_3002_remove_form_elements_fbv(self): + """Test remove form element.""" + self._test_3002_remove_form_elements() + + def _test_3003_edit_form_elements(self): """Test edit form element.""" db_clean_up() # Clean up database + @print_info + def test_3003_edit_form_elements(self): + """Test edit form element.""" + self._test_3003_edit_form_elements() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_3003_edit_form_elements_fbv(self): + """Test edit form element.""" + self._test_3003_edit_form_elements() + # class FormHandlerSpecificFobiBrowserBuldDynamicFormsTest( # BaseFobiBrowserBuldDynamicFormsTest): # """Form handler specific.""" @@ -657,8 +770,7 @@ def test_3003_edit_form_elements(self): # ++++++++++++++++++++ Form handler specific ++++++++++++++++++++ # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @print_info - def test_4001_add_form_handlers(self, wait=WAIT_FOR): + def _test_4001_add_form_handlers(self, wait=WAIT_FOR): """Test of adding a single form handler. At this point, if form isn't created, it should be. @@ -670,7 +782,23 @@ def test_4001_add_form_handlers(self, wait=WAIT_FOR): self._sleep(wait) @print_info - def test_4002_remove_form_handlers(self): + def test_4001_add_form_handlers(self, wait=WAIT_FOR): + """Test of adding a single form handler. + + At this point, if form isn't created, it should be. + """ + self._test_4001_add_form_handlers() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_4001_add_form_handlers_fbv(self, wait=WAIT_FOR): + """Test of adding a single form handler. + + At this point, if form isn't created, it should be. + """ + self._test_4001_add_form_handlers() + + def _test_4002_remove_form_handlers(self): """Test remove form handler.""" db_clean_up() # Clean up database @@ -679,9 +807,27 @@ def test_4002_remove_form_handlers(self): self._test_remove_form_handlers() @print_info - def test_4003_edit_form_handlers(self): + def test_4002_remove_form_handlers(self): + """Test remove form handler.""" + self._test_4002_remove_form_handlers() + + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_4002_remove_form_handlers_fbv(self): + """Test remove form handler.""" + self._test_4002_remove_form_handlers() + + def _test_4003_edit_form_handlers(self): """Test edit form handler.""" + # TODO + @print_info + def test_4003_edit_form_handlers(self): + """Test edit form handler.""" + self._test_4003_edit_form_handlers() -if __name__ == "__main__": - unittest.main() + @override_settings(ROOT_URLCONF="urls.function_based") + @print_info + def test_4003_edit_form_handlers_fbv(self): + """Test edit form handler.""" + self._test_4003_edit_form_handlers() diff --git a/src/fobi/tests/test_core.py b/src/fobi/tests/test_core.py index d935a3cc1..e09a87046 100644 --- a/src/fobi/tests/test_core.py +++ b/src/fobi/tests/test_core.py @@ -1,5 +1,4 @@ import datetime -import unittest from django.test import RequestFactory, TestCase from django.urls import reverse @@ -20,7 +19,7 @@ __title__ = "fobi.tests.test_core" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("FobiCoreTest",) @@ -196,7 +195,3 @@ def test_08_form_entry_is_active(self): form_entry.active_date_from = None form_entry.active_date_to = now self.assertFalse(form_entry.is_active) - - -if __name__ == "__main__": - unittest.main() diff --git a/src/fobi/tests/test_dynamic_forms.py b/src/fobi/tests/test_dynamic_forms.py index 9a3159cf7..b8297054c 100644 --- a/src/fobi/tests/test_dynamic_forms.py +++ b/src/fobi/tests/test_dynamic_forms.py @@ -1,5 +1,3 @@ -import unittest - from django.test import TestCase from .core import print_info @@ -14,7 +12,7 @@ __title__ = "fobi.tests.test_dynamic_forms" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("FobiDynamicFormsTest",) @@ -51,7 +49,3 @@ def test_01_assemble_form_class_and_render_form(self): flow.append(rendered_form) return flow - - -if __name__ == "__main__": - unittest.main() diff --git a/src/fobi/tests/test_feincms_integration.py b/src/fobi/tests/test_feincms_integration.py index 94108cb71..a48ee5a92 100644 --- a/src/fobi/tests/test_feincms_integration.py +++ b/src/fobi/tests/test_feincms_integration.py @@ -1,14 +1,15 @@ import logging -import unittest -import factories +from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait +import factories + from .base import BaseFobiBrowserBuldDynamicFormsTest -__title__ = "fobi.tests.test_browser_build_dynamic_forms" +__title__ = "fobi.tests.test_feincms_integration" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("FeinCMSIntegrationTest",) @@ -36,7 +37,8 @@ def test_fobi_form_widget_public_form(self): self.driver.get(self.fobi_form_page_url) # Wait until the edit widget form opens WebDriverWait(self.driver, timeout=TIMEOUT).until( - lambda driver: driver.find_element_by_xpath( + lambda driver: driver.find_element( + By.XPATH, '//body[contains(@class, "theme-bootstrap3")]' ) ) @@ -47,12 +49,9 @@ def test_fobi_form_widget_public_form(self): # self.driver.get(self.fobi_form_page_url) # # Wait until the edit widget form opens # WebDriverWait(self.driver, timeout=TIMEOUT).until( - # lambda driver: driver.find_element_by_xpath( + # lambda driver: driver.find_element( + # By.XPATH, # '//body[contains(@class, "theme-bootstrap3")]' # ) # ) # # TODO: - - -if __name__ == "__main__": - unittest.main() diff --git a/src/fobi/tests/test_form_importers_mailchimp.py b/src/fobi/tests/test_form_importers_mailchimp.py index f9416bf48..23c0b565c 100644 --- a/src/fobi/tests/test_form_importers_mailchimp.py +++ b/src/fobi/tests/test_form_importers_mailchimp.py @@ -1,5 +1,3 @@ -import unittest - # from django.contrib.auth import get_user_model from django.test import TestCase @@ -14,7 +12,7 @@ __title__ = "fobi.tests.test_form_importers_mailchimp" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("FormImportersMailchimpTest",) @@ -45,7 +43,3 @@ def test_01_test_mailchimp_importer(self): form_entry = FormEntry.objects.get(**form_properties) self.assertIsNotNone(form_entry.pk) - - -if __name__ == "__main__": - unittest.main() diff --git a/src/fobi/tests/test_sortable_dict.py b/src/fobi/tests/test_sortable_dict.py index 9b3378526..07d6169f8 100644 --- a/src/fobi/tests/test_sortable_dict.py +++ b/src/fobi/tests/test_sortable_dict.py @@ -1,4 +1,3 @@ -import unittest from copy import copy from django.test import TestCase @@ -9,7 +8,7 @@ __title__ = "fobi.tests.test_dynamic_forms" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ("FobiDataStructuresTest",) @@ -65,7 +64,3 @@ def test_02_sortable_dict_move_after_key(self): self.assertTrue(self.initial == self.expected) return flow - - -if __name__ == "__main__": - unittest.main() diff --git a/src/fobi/utils.py b/src/fobi/utils.py index 268f94de4..8e9031d36 100644 --- a/src/fobi/utils.py +++ b/src/fobi/utils.py @@ -48,7 +48,7 @@ __title__ = "fobi.utils" __author__ = "Artur Barseghyan " -__copyright__ = "2014-2019 Artur Barseghyan" +__copyright__ = "2014-2022 Artur Barseghyan" __license__ = "GPL 2.0/LGPL 2.1" __all__ = ( "append_edit_and_delete_links_to_field", diff --git a/src/fobi/views/function_based.py b/src/fobi/views/function_based.py index e1ef77d88..861361545 100644 --- a/src/fobi/views/function_based.py +++ b/src/fobi/views/function_based.py @@ -19,7 +19,6 @@ from django.urls import reverse from django.utils.datastructures import MultiValueDictKeyError from django.utils.translation import gettext, gettext_lazy as _ -from django_nine import versions from formtools.wizard.forms import ManagementForm from ..base import ( # get_registered_form_handler_plugins diff --git a/src/fobi/wizard/views/dynamic.py b/src/fobi/wizard/views/dynamic.py index 159c580a3..65db1d173 100644 --- a/src/fobi/wizard/views/dynamic.py +++ b/src/fobi/wizard/views/dynamic.py @@ -10,7 +10,6 @@ from django.utils.decorators import classonlymethod from django.utils.translation import gettext as _ from django.views.generic import TemplateView -from django_nine import versions from formtools.wizard.forms import ManagementForm from formtools.wizard.storage import get_storage from formtools.wizard.storage.exceptions import NoFileStorageConfigured diff --git a/tox.ini b/tox.ini index 1363b1ed5..752b8e61f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = py{36,37,38,39}-django{22,30,31,32} - py{38,39}-django{40} + py{38,39,310,311}-django{40,41} #flake8, #isort @@ -14,11 +14,15 @@ deps = django31: -r{toxinidir}/examples/requirements/django_3_1.txt django32: -r{toxinidir}/examples/requirements/django_3_2.txt django40: -r{toxinidir}/examples/requirements/django_4_0.txt + django41: -r{toxinidir}/examples/requirements/django_4_1.txt commands = -# {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.test --traceback -v 3 - pip install -e . +; {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.test --traceback -v 3 +; {envpython} -m pip install https://github.com/barseghyanartur/get-chromedriver-py/archive/main.tar.gz + {envpython} -m pip install get-chromedriver-py + get-chromedriver -vvvv + {envpython} -m pip install -e . ; {envpython} runtests.py - pytest + {envpython} -m pytest #[testenv:flake8] #basepython = python3.5 From 1e0edbb8426f8b360ea54b5ec660468e00734bdb Mon Sep 17 00:00:00 2001 From: Artur Barseghyan Date: Tue, 29 Nov 2022 00:48:41 +0100 Subject: [PATCH 3/3] Up setup.py --- setup.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c24a0150c..0671e2b42 100644 --- a/setup.py +++ b/setup.py @@ -246,17 +246,23 @@ long_description="{0}{1}".format(readme, screenshots), classifiers=[ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Environment :: Web Environment", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "License :: OSI Approved :: GNU Lesser General Public License v2 or " "later (LGPLv2+)", "Framework :: Django", + "Framework :: Django :: 2.2", + "Framework :: Django :: 3.0", + "Framework :: Django :: 3.1", + "Framework :: Django :: 3.2", + "Framework :: Django :: 4.0", + "Framework :: Django :: 4.1", "Intended Audience :: Developers", "Operating System :: OS Independent", "Development Status :: 4 - Beta",