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.
This commit is contained in:
Artur Barseghyan 2022-11-29 00:34:04 +01:00 committed by GitHub
parent 6ef902cc36
commit 12df02e6bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 986 additions and 206 deletions

77
.github/workflows/optional.yml vendored Normal file
View file

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

View file

@ -1,4 +1,4 @@
name: test name: Test
on: [push, pull_request] on: [push, pull_request]
@ -23,6 +23,7 @@ jobs:
- 5432:5432 - 5432:5432
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
max-parallel: 4 max-parallel: 4
matrix: matrix:
include: include:
@ -84,18 +85,35 @@ jobs:
- python-version: 3.9 - python-version: 3.9
requirements: django_4_0 requirements: django_4_0
tox_env: py39-django40 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: steps:
- name: Install libxml2 and libxslt
run: sudo apt-get install -y libxml2-dev libxslt-dev
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- uses: nanasess/setup-chromedriver@master - uses: nanasess/setup-chromedriver@master
with:
# Optional: do not specify to match Chrome's version
chromedriver-version: '102.0.5005.61'
- name: Install Dependencies - name: Install Dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
@ -110,6 +128,8 @@ jobs:
POSTGRES_HOST: localhost POSTGRES_HOST: localhost
POSTGRES_PORT: 5432 POSTGRES_PORT: 5432
- name: Coveralls - name: Coveralls
id: coveralls-setup
continue-on-error: true
uses: AndreMiras/coveralls-python-action@develop uses: AndreMiras/coveralls-python-action@develop
with: with:
parallel: true parallel: true
@ -120,6 +140,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Coveralls Finished - name: Coveralls Finished
id: coveralls-finish
continue-on-error: true
if: strategy.steps.coveralls-setup.outcome == 'success'
env: env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
GITHUB_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}

View file

@ -15,6 +15,15 @@ are used for versioning (schema follows below):
0.3.4 to 0.4). 0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document. - 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 0.19.5
------ ------
2022-11-20 2022-11-20

View file

@ -63,6 +63,9 @@ build-%: prepare-required-files
stop: stop:
docker-compose -f docker-compose.yml stop; docker-compose -f docker-compose.yml stop;
touch:
docker-compose -f docker-compose.yml exec backend touch manage.py
make-migrations: make-migrations:
docker-compose -f docker-compose.yml exec backend ./manage.py makemigrations $(APP); docker-compose -f docker-compose.yml exec backend ./manage.py makemigrations $(APP);
@ -72,6 +75,12 @@ migrate:
test: test:
docker-compose -f docker-compose.yml exec backend pytest /backend/src/$(TEST_PATH); 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: show-migrations:
docker-compose -f docker-compose.yml exec backend ./manage.py showmigrations docker-compose -f docker-compose.yml exec backend ./manage.py showmigrations
@ -93,6 +102,33 @@ pip-install:
pip-list: pip-list:
docker-compose -f docker-compose.yml exec backend 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: black:
docker-compose -f docker-compose.yml exec backend black . docker-compose -f docker-compose.yml exec backend black .
@ -105,3 +141,8 @@ bash:
prepare-required-files: prepare-required-files:
mkdir -p examples/logs examples/db examples/media examples/media/static examples/media/fobi_plugins/content_image mkdir -p examples/logs examples/db examples/media examples/media/static examples/media/fobi_plugins/content_image
mkdir -p examples/media/fobi_plugins/file mkdir -p examples/media/fobi_plugins/file
release:
python setup.py register
python setup.py sdist bdist_wheel
twine upload dist/* --verbose

View file

@ -34,8 +34,8 @@ handling the submitted form data).
Prerequisites Prerequisites
============= =============
- Django 2.2, 3.0, 3.1, 3.2 and 4.0. - Django 2.2, 3.0, 3.1, 3.2, 4.0 and 4.1.
- Python 3.6, 3.7, 3.8 and 3.9. - Python 3.6, 3.7, 3.8, 3.9, 3.10 and 3.11.
Key concepts Key concepts
============ ============

View file

@ -1,4 +1,4 @@
FROM docker.io/python:3.9 FROM docker.io/python:3.10-slim
ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
@ -12,17 +12,8 @@ RUN apt-get update && \
nano \ nano \
chromium \ chromium \
graphviz \ graphviz \
libpq-dev libpq-dev \
python3.9
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
RUN pip install pip --upgrade RUN pip install pip --upgrade
RUN pip install virtualenv RUN pip install virtualenv
@ -30,7 +21,7 @@ RUN pip install virtualenv
RUN mkdir /backend RUN mkdir /backend
WORKDIR /backend WORKDIR /backend
ADD examples/requirements/ /backend/requirements/ 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 "import geckodriver_autoinstaller; print(geckodriver_autoinstaller.install())"
RUN python -c "from chromedriver_py import binary_path; print(binary_path)" RUN python -c "from chromedriver_py import binary_path; print(binary_path)"
COPY . /backend/ COPY . /backend/

View file

@ -18,5 +18,7 @@ pillow==8.2.0
# via django-simple-captcha # via django-simple-captcha
pytz==2021.1 pytz==2021.1
# via django # via django
six==1.16.0
# via django-simple-captcha
sqlparse==0.4.1 sqlparse==0.4.1
# via django # via django

View file

@ -4,6 +4,7 @@ bleach>=2.1.2
decorator>=4.0.4 decorator>=4.0.4
docopt>=0.4.0 docopt>=0.4.0
docutils>=0.12 docutils>=0.12
importlib-metadata<5.0.0
Jinja2>=2.8 Jinja2>=2.8
mailchimp>=2.0.9 mailchimp>=2.0.9
markdown markdown

View file

@ -244,6 +244,7 @@ six==1.16.0
# via # via
# -r common.in # -r common.in
# bleach # bleach
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

@ -223,7 +223,7 @@ pluggy==0.13.1
# tox # tox
pre-commit==2.13.0 pre-commit==2.13.0
# via -r django_3_2.txt # via -r django_3_2.txt
psycopg2-binary==2.9.1 psycopg2-binary==2.8.6
# via -r django_3_2.txt # via -r django_3_2.txt
ptyprocess==0.7.0 ptyprocess==0.7.0
# via # via
@ -300,6 +300,7 @@ six==1.16.0
# via # via
# -r django_3_2.txt # -r django_3_2.txt
# bleach # bleach
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

@ -238,6 +238,7 @@ six==1.16.0
# via # via
# -r common.in # -r common.in
# bleach # bleach
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

@ -244,6 +244,7 @@ six==1.16.0
# via # via
# -r common.in # -r common.in
# bleach # bleach
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

@ -244,6 +244,7 @@ six==1.16.0
# via # via
# -r common.in # -r common.in
# bleach # bleach
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

@ -244,6 +244,7 @@ six==1.16.0
# via # via
# -r common.in # -r common.in
# bleach # bleach
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

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

View file

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

View file

@ -271,6 +271,7 @@ six==1.16.0
# -r common.in # -r common.in
# bleach # bleach
# django-fobi # django-fobi
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

@ -1,6 +1,7 @@
Faker Faker
coverage coverage
factory_boy factory_boy
importlib-metadata<5.0.0
py py
pytest pytest
pytest-cov pytest-cov
@ -9,5 +10,5 @@ pytest-ordering
pytest-pythonpath pytest-pythonpath
selenium selenium
tox tox
chromedriver-py==102.0.5005.61 chromedriver-py==107.0.5304.62
#geckodriver-autoinstaller #geckodriver-autoinstaller

View file

@ -244,6 +244,7 @@ six==1.16.0
# via # via
# -r common.in # -r common.in
# bleach # bleach
# django-simple-captcha
# feincms # feincms
# python-dateutil # python-dateutil
# tox # tox

View file

@ -3,9 +3,7 @@ import uuid
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django_nine import versions
from fobi.base import get_theme from fobi.base import get_theme
from fobi.helpers import handle_uploaded_file from fobi.helpers import handle_uploaded_file

View file

@ -1,7 +1,7 @@
# Django settings for example project. # Django settings for example project.
import os 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 from selenium import webdriver
@ -699,10 +699,7 @@ if DEBUG and DEBUG_TOOLBAR:
pass pass
# Only now make proper assignments # Only now make proper assignments
if DJANGO_GTE_2_0: MIDDLEWARE = _MIDDLEWARE
MIDDLEWARE = _MIDDLEWARE
else:
MIDDLEWARE_CLASSES = _MIDDLEWARE
if DEBUG: if DEBUG:
try: try:

View file

@ -0,0 +1 @@
from .class_based import *

View file

@ -10,6 +10,10 @@ from django_nine import versions
from fobi.settings import DEFAULT_THEME from fobi.settings import DEFAULT_THEME
__all__ = (
"urlpatterns",
)
admin.autodiscover() admin.autodiscover()
# Mapping. # Mapping.
@ -60,14 +64,9 @@ url_patterns_args = [
), ),
] ]
if versions.DJANGO_GTE_2_0: url_patterns_args += [
url_patterns_args += [ url(r"^admin/", admin.site.urls),
url(r"^admin/", admin.site.urls), ]
]
else:
url_patterns_args += [
url(r"^admin/", include(admin.site.urls)),
]
url_patterns_args += [ url_patterns_args += [
# django-registration URLs: # django-registration URLs:

View file

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

View file

@ -11,6 +11,7 @@ pip-compile django_3_0.in "$@"
pip-compile django_3_1.in "$@" pip-compile django_3_1.in "$@"
pip-compile django_3_2.in "$@" pip-compile django_3_2.in "$@"
pip-compile django_4_0.in "$@" pip-compile django_4_0.in "$@"
pip-compile django_4_1.in "$@"
pip-compile djangocms_3_4_3.in "$@" pip-compile djangocms_3_4_3.in "$@"
pip-compile djangorestframework.in "$@" pip-compile djangorestframework.in "$@"
pip-compile docs.in "$@" pip-compile docs.in "$@"

View file

@ -3,7 +3,7 @@ import os
from distutils.version import LooseVersion from distutils.version import LooseVersion
from setuptools import setup, find_packages from setuptools import setup, find_packages
version = "0.19.5" version = "0.19.6"
# *************************************************************************** # ***************************************************************************
# ************************** Django version ********************************* # ************************** Django version *********************************
@ -96,7 +96,7 @@ try:
".. figure:: https://github.com/barseghyanartur/django-fobi/raw/" ".. figure:: https://github.com/barseghyanartur/django-fobi/raw/"
"main/docs/_static", "main/docs/_static",
) )
except: except OSError:
readme = "" readme = ""
screenshots = "" screenshots = ""
@ -251,6 +251,7 @@ setup(
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Environment :: Web Environment", "Environment :: Web Environment",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
"License :: OSI Approved :: GNU Lesser General Public License v2 or " "License :: OSI Approved :: GNU Lesser General Public License v2 or "

View file

@ -1,5 +1,5 @@
__title__ = "django-fobi" __title__ = "django-fobi"
__version__ = "0.19.5" __version__ = "0.19.6"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2022 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"

View file

@ -2,12 +2,10 @@ from django.contrib import admin, messages
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.template import RequestContext
from django.urls import re_path as url from django.urls import re_path as url
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_nine import versions
from .constants import ACTION_CHOICE_REPLACE from .constants import ACTION_CHOICE_REPLACE
from .forms import ( from .forms import (
@ -71,7 +69,7 @@ def base_bulk_change_plugins(
opts = modeladmin.model._meta opts = modeladmin.model._meta
app_label = opts.app_label 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) post = dict(request.POST)
if selected: if selected:
post["selected_plugins"] = ",".join(selected) post["selected_plugins"] = ",".join(selected)
@ -577,7 +575,9 @@ class BasePluginModelAdmin(admin.ModelAdmin):
class FormElementAdmin(BasePluginModelAdmin): class FormElementAdmin(BasePluginModelAdmin):
"""FormElement admin.""" """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): def _get_bulk_change_form_class(self):
"""Get bulk change form class.""" """Get bulk change form class."""
@ -614,7 +614,9 @@ admin.site.register(FormElement, FormElementAdmin)
class FormHandlerAdmin(BasePluginModelAdmin): class FormHandlerAdmin(BasePluginModelAdmin):
"""FormHandler admin.""" """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): def _get_bulk_change_form_class(self):
"""Get bulk change form class.""" """Get bulk change form class."""
@ -651,9 +653,9 @@ admin.site.register(FormHandler, FormHandlerAdmin)
class FormWizardHandlerAdmin(BasePluginModelAdmin): class FormWizardHandlerAdmin(BasePluginModelAdmin):
"""FormHandler admin.""" """FormHandler admin."""
actions = [ actions = (bulk_change_form_wizard_handler_plugins,) + tuple(
bulk_change_form_wizard_handler_plugins BasePluginModelAdmin.actions
] + BasePluginModelAdmin.actions )
def _get_bulk_change_form_class(self): def _get_bulk_change_form_class(self):
"""Get bulk change form class.""" """Get bulk change form class."""

View file

@ -61,7 +61,7 @@ from .settings import ( # FAIL_ON_ERRORS_IN_FORM_ELEMENT_PLUGINS,
__title__ = "fobi.base" __title__ = "fobi.base"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ( __all__ = (
"assemble_form_field_widget_class", "assemble_form_field_widget_class",

View file

@ -1,7 +1,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import copy import copy
from collections import Mapping, OrderedDict from collections import OrderedDict
from collections.abc import Mapping
import six import six
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
@ -24,7 +25,7 @@ from . import UID as INTEGRATE_WITH_UID
__title__ = "fobi.contrib.apps.drf_integration.dynamic" __title__ = "fobi.contrib.apps.drf_integration.dynamic"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ( __all__ = (
"assemble_serializer_class", "assemble_serializer_class",

View file

@ -1,5 +1,5 @@
from django.utils.translation import gettext_lazy as _ 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 .......base import IntegrationFormFieldPlugin
from .... import UID as INTEGRATE_WITH_UID from .... import UID as INTEGRATE_WITH_UID
@ -43,9 +43,10 @@ class NullBooleanSelectPlugin(
"initial": form_element_plugin.data.initial, "initial": form_element_plugin.data.initial,
"label": form_element_plugin.data.label, "label": form_element_plugin.data.label,
"help_text": form_element_plugin.data.help_text, "help_text": form_element_plugin.data.help_text,
"allow_null": True,
} }
return [ return [
DRFIntegrationFormElementPluginProcessor( DRFIntegrationFormElementPluginProcessor(
field_class=NullBooleanField, field_kwargs=field_kwargs field_class=BooleanField, field_kwargs=field_kwargs
) )
] ]

View file

@ -1,4 +1,3 @@
# from __future__ import unicode_literals
from collections import OrderedDict from collections import OrderedDict
from rest_framework import serializers from rest_framework import serializers
@ -8,7 +7,7 @@ from ....models import FormEntry
__title__ = "fobi.contrib.apps.drf_integration.serializers" __title__ = "fobi.contrib.apps.drf_integration.serializers"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FormEntrySerializer",) __all__ = ("FormEntrySerializer",)

View file

@ -1,8 +1,6 @@
# from __future__ import unicode_literals
from django.contrib import messages from django.contrib import messages
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.translation import gettext from django.utils.translation import gettext
from django_nine import versions
from rest_framework import mixins, permissions from rest_framework import mixins, permissions
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet from rest_framework.viewsets import GenericViewSet

View file

@ -1,4 +1,3 @@
# from mezzanine.conf import settings
from mezzanine.pages.page_processors import processor_for from mezzanine.pages.page_processors import processor_for
from .models import FobiFormPage from .models import FobiFormPage

View file

@ -22,7 +22,7 @@ from fobi.helpers import clone_file, delete_file
__title__ = "fobi.contrib.plugins.form_elements.content.content_image.base" __title__ = "fobi.contrib.plugins.form_elements.content.content_image.base"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("ContentImagePlugin",) __all__ = ("ContentImagePlugin",)

View file

@ -15,7 +15,7 @@ from fobi.base import FormElementPlugin
__title__ = "fobi.contrib.plugins.form_elements.content.content_image_url.base" __title__ = "fobi.contrib.plugins.form_elements.content.content_image_url.base"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("ContentImageURLPlugin",) __all__ = ("ContentImageURLPlugin",)

View file

@ -14,7 +14,7 @@ from fobi.reusable.markdown_widget.helpers import convert_to_markdown
__title__ = "fobi.contrib.plugins.form_elements.content.content_richtext.base" __title__ = "fobi.contrib.plugins.form_elements.content.content_richtext.base"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("ContentMarkdownPlugin",) __all__ = ("ContentMarkdownPlugin",)

View file

@ -14,7 +14,7 @@ from fobi.base import FormElementPlugin
__title__ = "fobi.contrib.plugins.form_elements.content.content_text.base" __title__ = "fobi.contrib.plugins.form_elements.content.content_text.base"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("ContentTextPlugin",) __all__ = ("ContentTextPlugin",)

View file

@ -14,7 +14,7 @@ from fobi.base import FormElementPlugin
__title__ = "fobi.contrib.plugins.form_elements.content.content_video.base" __title__ = "fobi.contrib.plugins.form_elements.content.content_video.base"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("ContentVideoPlugin",) __all__ = ("ContentVideoPlugin",)

View file

@ -5,7 +5,6 @@ from django.forms.utils import flatatt
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_nine import versions
from six import PY3, text_type from six import PY3, text_type
from . import UID from . import UID

View file

@ -1,9 +1,6 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import render 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 ( from .....base import (
get_form_handler_plugin_widget, get_form_handler_plugin_widget,
get_form_wizard_handler_plugin_widget, get_form_wizard_handler_plugin_widget,

View file

@ -15,7 +15,7 @@ from .constants import WIZARD_TYPE_COOKIE, WIZARD_TYPE_SESSION
__title__ = "fobi.dynamic" __title__ = "fobi.dynamic"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ( __all__ = (
"assemble_form_class", "assemble_form_class",

View file

@ -5,7 +5,6 @@ from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
from django_nine import versions
class Migration(migrations.Migration): class Migration(migrations.Migration):

View file

@ -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 <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
if __name__ == "__main__":
unittest.main()

View file

@ -7,6 +7,7 @@ from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.core.management import call_command from django.core.management import call_command
from django.urls import reverse from django.urls import reverse
from selenium import webdriver from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
@ -143,16 +144,17 @@ class BaseFobiBrowserBuldDynamicFormsTest(StaticLiveServerTestCase):
"{0}{1}".format(self._get_live_server_url(), reverse("auth_login")) "{0}{1}".format(self._get_live_server_url(), reverse("auth_login"))
) )
self._maximize_window() 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) 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) 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 # Wait until the list view opens
WebDriverWait(self.driver, timeout=TIMEOUT).until( WebDriverWait(self.driver, timeout=TIMEOUT).until(
# lambda driver: driver.find_element_by_id('id_main') # lambda driver: driver.find_element(By.ID, 'id_main')
lambda driver: driver.find_element_by_xpath( lambda driver: driver.find_element(
By.XPATH,
'//body[contains(@class, "theme")]' '//body[contains(@class, "theme")]'
) )
) )
@ -196,12 +198,12 @@ class BaseFobiBrowserBuldDynamicFormsTest(StaticLiveServerTestCase):
def _scroll_page_top(self): def _scroll_page_top(self):
"""Scroll to the page top.""" """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) html.send_keys(Keys.HOME)
def _scroll_page_bottom(self): def _scroll_page_bottom(self):
"""Scroll to the page bottom.""" """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) html.send_keys(Keys.END)
def take_screenshot(self, name="screenshot"): def take_screenshot(self, name="screenshot"):

View file

@ -99,7 +99,7 @@ from fobi.contrib.plugins.form_handlers.mail_sender.fobi_form_handlers import (
__title__ = "fobi.tests.data" __title__ = "fobi.tests.data"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ( __all__ = (
"TEST_DYNAMIC_FORMS_DEFINITION_DATA", "TEST_DYNAMIC_FORMS_DEFINITION_DATA",
@ -252,10 +252,13 @@ TEST_FORM_ELEMENT_PLUGIN_DATA = {
# }, # },
} }
# 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_FORM_FIELD_DATA = {
"test_boolean": True, "test_boolean": True,
# 'test_checkbox_select_multiple_input': '', # '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( "test_datetime_input": datetime.datetime.now().strftime(
"%Y-%m-%d %H:%M:%S" "%Y-%m-%d %H:%M:%S"
), ),
@ -313,7 +316,7 @@ TEST_FORM_HANDLER_PLUGIN_DATA = OrderedDict(
), ),
( (
force_str(HTTPRepostHandlerPlugin.name), force_str(HTTPRepostHandlerPlugin.name),
{"endpoint_url": "http://dev.example.com"}, {"endpoint_url": "https://webhook.site/a91e7062-83c4"},
), ),
] ]
) )

View file

@ -1,14 +1,12 @@
import logging import logging
import os
import unittest
from datetime import datetime
from django.conf import settings
from django.urls import reverse from django.urls import reverse
from selenium.webdriver.common.action_chains import ActionChains from django.test import override_settings
# from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support.wait import WebDriverWait
from fobi.models import FormEntry
from . import constants from . import constants
from .base import BaseFobiBrowserBuldDynamicFormsTest from .base import BaseFobiBrowserBuldDynamicFormsTest
from .core import print_info from .core import print_info
@ -19,11 +17,9 @@ from .data import (
) )
from .helpers import db_clean_up from .helpers import db_clean_up
from fobi.models import FormEntry
__title__ = "fobi.tests.test_browser_build_dynamic_forms" __title__ = "fobi.tests.test_browser_build_dynamic_forms"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FobiBrowserBuldDynamicFormsTest",) __all__ = ("FobiBrowserBuldDynamicFormsTest",)
@ -57,7 +53,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Wait until the edit widget form opens # Wait until the edit widget form opens
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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")]' '//body[contains(@class, "theme-bootstrap3")]'
) )
) )
@ -71,14 +68,16 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Follow the create form link. # Follow the create form link.
# Click the button to go to dashboard edit # 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")]' '//a[contains(@class, "list-group-item")]'
).click() ).click()
# Wait until the dashboard edit view opens # Wait until the dashboard edit view opens
WebDriverWait(self.driver, timeout=TIMEOUT).until( WebDriverWait(self.driver, timeout=TIMEOUT).until(
# lambda driver: driver.find_element_by_id('id_main') # lambda driver: driver.find_element(By.ID, 'id_main')
lambda driver: driver.find_element_by_xpath( lambda driver: driver.find_element(
By.XPATH,
'//body[contains(@class, "theme-bootstrap3")]' '//body[contains(@class, "theme-bootstrap3")]'
) )
) )
@ -94,11 +93,11 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
if form_data: if form_data:
for field_name, field_value in form_data.items(): 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) field_input.send_keys(field_value)
# Click add widget button # 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( logger.debug(
"""//div[contains(text(), 'Form {0} was created """ """//div[contains(text(), 'Form {0} was created """
@ -109,7 +108,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Wait until the fobi page opens with the form element in # Wait until the fobi page opens with the form element in
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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 """ """//div[contains(text(), 'Form {0} was created """
"""successfully.') """ """successfully.') """
"""and contains(@class, "alert-info")]""".format( """and contains(@class, "alert-info")]""".format(
@ -138,14 +138,16 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
""" """
# # Wait until the add widget view opens # # Wait until the add widget view opens
# WebDriverWait(self.driver, timeout=TIMEOUT).until( # 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 """ # """//a[contains(text(), 'Choose form element to add') and """
# """contains(@class, "dropdown-toggle")]""" # """contains(@class, "dropdown-toggle")]"""
# ) # )
# ) # )
# try: # 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 """ # """//a[contains(text(), 'Choose form element to add') and """
# """contains(@class, "dropdown-toggle")]""" # """contains(@class, "dropdown-toggle")]"""
# ) # )
@ -154,7 +156,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Click the add form element button to add a new form element to the # Click the add form element button to add a new form element to the
# form. # 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 """ """//a[contains(text(), 'Choose form element to add') and """
"""contains(@class, "dropdown-toggle")]""" """contains(@class, "dropdown-toggle")]"""
) )
@ -164,19 +167,21 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Find the parent element # Find the parent element
add_form_element_parent_container = ( 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 # Find the container of the available form elements
add_form_element_available_elements_container = ( 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")]' '//ul[contains(@class, "dropdown-menu")]'
) )
) )
# Click on the element we want # Click on the element we want
form_element_to_add = ( 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) '//a[text()="{0}"]'.format(form_element_name)
) )
) )
@ -196,7 +201,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
if form_element_data: if form_element_data:
# Wait until the add widget view opens # Wait until the add widget view opens
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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 """ """//h1[contains(text(), 'Add "{0}" element to """
"""the form')]""".format(form_element_name) """the form')]""".format(form_element_name)
) )
@ -205,14 +211,15 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
for field_name, field_value in form_element_data.items(): for field_name, field_value in form_element_data.items():
# Wait until element is visible # Wait until element is visible
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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.clear()
field_input.send_keys(field_value) field_input.send_keys(field_value)
# Click add widget button # Click add widget button
submit_button = self.driver.find_element_by_xpath( submit_button = self.driver.find_element(
By.XPATH,
'//button[@type="submit"]' '//button[@type="submit"]'
) )
@ -229,7 +236,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Wait until the fobi page opens with the form element in. # Wait until the fobi page opens with the form element in.
self._maximize_window() self._maximize_window()
WebDriverWait(self.driver, timeout=LONG_TIMEOUT).until( 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}" """ """//div[contains(text(), 'The form element plugin "{0}" """
"""was added successfully.') """ """was added successfully.') """
"""and contains(@class, "alert-info")]""".format( """and contains(@class, "alert-info")]""".format(
@ -280,7 +288,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
""" """
# Get the label of the given form element in order to delete it later # Get the label of the given form element in order to delete it later
# from the form. # 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})') """ """//label[contains(text(), '({0})') """
"""and contains(@class, "control-label")]""".format( """and contains(@class, "control-label")]""".format(
form_element_name form_element_name
@ -289,12 +298,13 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Get the parent of the label # Get the parent of the label
delete_form_element_label_parent_container = ( 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 # Click the add form element button to add a new form element to the
# form. # 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"
) )
# delete_form_element_link.click() # delete_form_element_link.click()
@ -305,7 +315,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Wait until the fobi page opens with the form element in. # Wait until the fobi page opens with the form element in.
WebDriverWait(self.driver, timeout=LONG_TIMEOUT).until( 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}" """ """//div[contains(text(), 'The form element plugin "{0}" """
"""was deleted successfully.') """ """was deleted successfully.') """
"""and contains(@class, "alert-info")]""".format( """and contains(@class, "alert-info")]""".format(
@ -338,14 +349,16 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# tab with form handlers. Otherwise Selenium raises # tab with form handlers. Otherwise Selenium raises
# an exception about non-visible element on the page # an exception about non-visible element on the page
# that we're trying to fetch. # 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"]""" """//a[@href="#tab-form-handlers"]"""
) )
form_handlers_tab_link.click() form_handlers_tab_link.click()
# Click the add form element button to add a new form element to the # Click the add form element button to add a new form element to the
# form. # 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') """ """//a[contains(text(), 'Choose form handler to add') """
"""and contains(@class, "dropdown-toggle")]""" """and contains(@class, "dropdown-toggle")]"""
) )
@ -353,19 +366,21 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Find the parent element # Find the parent element
add_form_handler_parent_container = ( 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 # Find the container of the available form elements
add_form_handler_available_elements_container = ( 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")]' '//ul[contains(@class, "dropdown-menu")]'
) )
) )
# Click on the element we want # Click on the element we want
form_handler_to_add = ( 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) '//a[text()="{0}"]'.format(form_handler_name)
) )
) )
@ -375,7 +390,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
if form_handler_data: if form_handler_data:
# Wait until the add widget view opens # Wait until the add widget view opens
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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 """ """//h1[contains(text(), 'Add "{0}" handler to """
"""the form')]""".format(form_handler_name) """the form')]""".format(form_handler_name)
) )
@ -383,17 +399,19 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Config # Config
for field_name, field_value in form_handler_data.items(): 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) field_input.send_keys(field_value)
# Click add widget button # Click add widget button
self.driver.find_element_by_xpath( self.driver.find_element(
By.XPATH,
'//button[@type="submit"]' '//button[@type="submit"]'
).click() ).click()
# Wait until the fobi page opens with the form element in. # Wait until the fobi page opens with the form element in.
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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}" """ """//div[contains(text(), 'The form handler plugin "{0}" """
"""was added successfully.') """ """was added successfully.') """
"""and contains(@class, "alert-info")]""".format( """and contains(@class, "alert-info")]""".format(
@ -439,25 +457,28 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# tab with form handlers. Otherwise Selenium raises # tab with form handlers. Otherwise Selenium raises
# an exception about non-visible element on the page # an exception about non-visible element on the page
# that we're trying to fetch. # 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"]""" """//a[@href="#tab-form-handlers"]"""
) )
form_handlers_tab_link.click() form_handlers_tab_link.click()
# Get the label of the given form element in order to delete it later # Get the label of the given form element in order to delete it later
# from the form. # 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) """//td[contains(text(), '{0}')]""".format(form_handler_name)
) )
# Get the parent of the label # Get the parent of the label
delete_form_handler_label_parent_container = ( 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 # Click the add form element button to add a new form element to the
# form. # 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"
) )
delete_form_handler_link.click() delete_form_handler_link.click()
@ -466,7 +487,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Wait until the fobi page opens with the form element in. # Wait until the fobi page opens with the form element in.
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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}" """ """//div[contains(text(), 'The form handler plugin "{0}" """
"""was deleted successfully.') """ """was deleted successfully.') """
"""and contains(@class, "alert-info")]""".format( """and contains(@class, "alert-info")]""".format(
@ -496,10 +518,20 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# +++++++++++++++++++++++++++ General +++++++++++++++++++++++++++ # +++++++++++++++++++++++++++ General +++++++++++++++++++++++++++
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def _test_1001_open_dashboard(self):
"""Test open dashboard."""
self._go_to_dashboard()
@print_info @print_info
def test_1001_open_dashboard(self): def test_1001_open_dashboard(self):
"""Test open dashboard.""" """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( # class GeneralFobiBrowserBuldDynamicFormsTest(
# BaseFobiBrowserBuldDynamicFormsTest): # BaseFobiBrowserBuldDynamicFormsTest):
@ -514,8 +546,7 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# ++++++++++++++++++++++ Form specific ++++++++++++++++++++++++++ # ++++++++++++++++++++++ Form specific ++++++++++++++++++++++++++
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@print_info def _test_2001_add_form(self):
def test_2001_add_form(self):
"""Test add a new form.""" """Test add a new form."""
# Clean up database # Clean up database
db_clean_up() db_clean_up()
@ -523,24 +554,55 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
self._test_add_form(wait=WAIT_FOR) self._test_add_form(wait=WAIT_FOR)
# Make sure the success message is there # 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( # """//div[text()='Form {0} was created successfully.']""".format(
# constants.TEST_FORM_NAME # 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 @print_info
def test_2002_edit_form(self): def test_2002_edit_form(self):
"""Test edit a form.""" """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 # TODO
@print_info @print_info
def test_2003_delete_form(self): def test_2003_delete_form(self):
"""Test delete a form.""" """Test delete a form."""
# TODO self._test_2003_delete_form()
@override_settings(ROOT_URLCONF="urls.function_based")
@print_info @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.""" """Test submit form."""
# Clean up database # Clean up database
db_clean_up() db_clean_up()
@ -560,31 +622,34 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# Wait until the edit widget form opens # Wait until the edit widget form opens
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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")]' '//body[contains(@class, "theme-bootstrap3")]'
) )
) )
for field_name, field_value in TEST_FORM_FIELD_DATA.items(): 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) field_input.send_keys(field_value)
self.take_screenshot("filled_form_page") self.take_screenshot("filled_form_page")
self._sleep(2) self._sleep(2)
footer = self.driver.find_element_by_xpath("//footer") footer = self.driver.find_element(By.XPATH, "//footer")
footer.click() footer.click()
self._scroll_page_bottom() self._scroll_page_bottom()
# Wait until button is there # Wait until button is there
WebDriverWait(self.driver, timeout=TIMEOUT).until( WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath( lambda driver: driver.find_element(
By.XPATH,
'//button[@type="submit"]' '//button[@type="submit"]'
) )
) )
# Click add widget button # Click add widget button
submit_button = self.driver.find_element_by_xpath( submit_button = self.driver.find_element(
By.XPATH,
'//button[@type="submit"]' '//button[@type="submit"]'
) )
@ -606,7 +671,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# twice as long as the normal timeout. # twice as long as the normal timeout.
self.take_screenshot("submit_success_page") self.take_screenshot("submit_success_page")
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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 """ """//div[contains(text(), 'Form {0} was submitted """
"""successfully.') """ """successfully.') """
"""and contains(@class, "alert-info")]""".format( """and contains(@class, "alert-info")]""".format(
@ -621,12 +687,28 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# BaseFobiBrowserBuldDynamicFormsTest): # BaseFobiBrowserBuldDynamicFormsTest):
# """Form element specific.""" # """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 ++++++++++++++++++++ # ++++++++++++++++++++ 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.""" """Test adding form elements."""
db_clean_up() # Clean up database db_clean_up() # Clean up database
@ -635,7 +717,17 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
self._sleep(wait) self._sleep(wait)
@print_info @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.""" """Test remove form element."""
# Clean up database # Clean up database
db_clean_up() db_clean_up()
@ -645,10 +737,31 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
self._test_remove_form_elements() self._test_remove_form_elements()
@print_info @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.""" """Test edit form element."""
db_clean_up() # Clean up database 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( # class FormHandlerSpecificFobiBrowserBuldDynamicFormsTest(
# BaseFobiBrowserBuldDynamicFormsTest): # BaseFobiBrowserBuldDynamicFormsTest):
# """Form handler specific.""" # """Form handler specific."""
@ -657,8 +770,7 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# ++++++++++++++++++++ Form handler specific ++++++++++++++++++++ # ++++++++++++++++++++ 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. """Test of adding a single form handler.
At this point, if form isn't created, it should be. At this point, if form isn't created, it should be.
@ -670,7 +782,23 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
self._sleep(wait) self._sleep(wait)
@print_info @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.""" """Test remove form handler."""
db_clean_up() # Clean up database db_clean_up() # Clean up database
@ -678,10 +806,28 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
self._test_remove_form_handlers() self._test_remove_form_handlers()
@print_info
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 @print_info
def test_4003_edit_form_handlers(self): def test_4003_edit_form_handlers(self):
"""Test edit form handler.""" """Test edit form handler."""
self._test_4003_edit_form_handlers()
@override_settings(ROOT_URLCONF="urls.function_based")
if __name__ == "__main__": @print_info
unittest.main() def test_4003_edit_form_handlers_fbv(self):
"""Test edit form handler."""
self._test_4003_edit_form_handlers()

View file

@ -1,5 +1,4 @@
import datetime import datetime
import unittest
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from django.urls import reverse from django.urls import reverse
@ -20,7 +19,7 @@ from fobi.models import FormEntry, FormWizardEntry
__title__ = "fobi.tests.test_core" __title__ = "fobi.tests.test_core"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FobiCoreTest",) __all__ = ("FobiCoreTest",)
@ -196,7 +195,3 @@ class FobiCoreTest(TestCase):
form_entry.active_date_from = None form_entry.active_date_from = None
form_entry.active_date_to = now form_entry.active_date_to = now
self.assertFalse(form_entry.is_active) self.assertFalse(form_entry.is_active)
if __name__ == "__main__":
unittest.main()

View file

@ -1,5 +1,3 @@
import unittest
from django.test import TestCase from django.test import TestCase
from .core import print_info from .core import print_info
@ -14,7 +12,7 @@ from fobi.dynamic import assemble_form_class
__title__ = "fobi.tests.test_dynamic_forms" __title__ = "fobi.tests.test_dynamic_forms"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FobiDynamicFormsTest",) __all__ = ("FobiDynamicFormsTest",)
@ -51,7 +49,3 @@ class FobiDynamicFormsTest(TestCase):
flow.append(rendered_form) flow.append(rendered_form)
return flow return flow
if __name__ == "__main__":
unittest.main()

View file

@ -1,14 +1,15 @@
import logging import logging
import unittest
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
import factories import factories
from selenium.webdriver.support.wait import WebDriverWait
from .base import BaseFobiBrowserBuldDynamicFormsTest from .base import BaseFobiBrowserBuldDynamicFormsTest
__title__ = "fobi.tests.test_browser_build_dynamic_forms" __title__ = "fobi.tests.test_feincms_integration"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FeinCMSIntegrationTest",) __all__ = ("FeinCMSIntegrationTest",)
@ -36,7 +37,8 @@ class FeinCMSIntegrationTest(BaseFobiBrowserBuldDynamicFormsTest):
self.driver.get(self.fobi_form_page_url) self.driver.get(self.fobi_form_page_url)
# Wait until the edit widget form opens # Wait until the edit widget form opens
WebDriverWait(self.driver, timeout=TIMEOUT).until( 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")]' '//body[contains(@class, "theme-bootstrap3")]'
) )
) )
@ -47,12 +49,9 @@ class FeinCMSIntegrationTest(BaseFobiBrowserBuldDynamicFormsTest):
# self.driver.get(self.fobi_form_page_url) # self.driver.get(self.fobi_form_page_url)
# # Wait until the edit widget form opens # # Wait until the edit widget form opens
# WebDriverWait(self.driver, timeout=TIMEOUT).until( # 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")]' # '//body[contains(@class, "theme-bootstrap3")]'
# ) # )
# ) # )
# # TODO: # # TODO:
if __name__ == "__main__":
unittest.main()

View file

@ -1,5 +1,3 @@
import unittest
# from django.contrib.auth import get_user_model # from django.contrib.auth import get_user_model
from django.test import TestCase from django.test import TestCase
@ -14,7 +12,7 @@ from fobi.models import FormElementEntry, FormEntry
__title__ = "fobi.tests.test_form_importers_mailchimp" __title__ = "fobi.tests.test_form_importers_mailchimp"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FormImportersMailchimpTest",) __all__ = ("FormImportersMailchimpTest",)
@ -45,7 +43,3 @@ class FormImportersMailchimpTest(TestCase):
form_entry = FormEntry.objects.get(**form_properties) form_entry = FormEntry.objects.get(**form_properties)
self.assertIsNotNone(form_entry.pk) self.assertIsNotNone(form_entry.pk)
if __name__ == "__main__":
unittest.main()

View file

@ -1,4 +1,3 @@
import unittest
from copy import copy from copy import copy
from django.test import TestCase from django.test import TestCase
@ -9,7 +8,7 @@ from fobi.data_structures import SortableDict
__title__ = "fobi.tests.test_dynamic_forms" __title__ = "fobi.tests.test_dynamic_forms"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FobiDataStructuresTest",) __all__ = ("FobiDataStructuresTest",)
@ -65,7 +64,3 @@ class FobiDataStructuresTest(TestCase):
self.assertTrue(self.initial == self.expected) self.assertTrue(self.initial == self.expected)
return flow return flow
if __name__ == "__main__":
unittest.main()

View file

@ -48,7 +48,7 @@ from .settings import (
__title__ = "fobi.utils" __title__ = "fobi.utils"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>" __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan" __copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1" __license__ = "GPL 2.0/LGPL 2.1"
__all__ = ( __all__ = (
"append_edit_and_delete_links_to_field", "append_edit_and_delete_links_to_field",

View file

@ -19,7 +19,6 @@ from django.template import RequestContext
from django.urls import reverse from django.urls import reverse
from django.utils.datastructures import MultiValueDictKeyError from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import gettext, gettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
from django_nine import versions
from formtools.wizard.forms import ManagementForm from formtools.wizard.forms import ManagementForm
from ..base import ( # get_registered_form_handler_plugins from ..base import ( # get_registered_form_handler_plugins

View file

@ -10,7 +10,6 @@ from django.urls import reverse
from django.utils.decorators import classonlymethod from django.utils.decorators import classonlymethod
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django_nine import versions
from formtools.wizard.forms import ManagementForm from formtools.wizard.forms import ManagementForm
from formtools.wizard.storage import get_storage from formtools.wizard.storage import get_storage
from formtools.wizard.storage.exceptions import NoFileStorageConfigured from formtools.wizard.storage.exceptions import NoFileStorageConfigured

12
tox.ini
View file

@ -1,7 +1,7 @@
[tox] [tox]
envlist = envlist =
py{36,37,38,39}-django{22,30,31,32} py{36,37,38,39}-django{22,30,31,32}
py{38,39}-django{40} py{38,39,310,311}-django{40,41}
#flake8, #flake8,
#isort #isort
@ -14,11 +14,15 @@ deps =
django31: -r{toxinidir}/examples/requirements/django_3_1.txt django31: -r{toxinidir}/examples/requirements/django_3_1.txt
django32: -r{toxinidir}/examples/requirements/django_3_2.txt django32: -r{toxinidir}/examples/requirements/django_3_2.txt
django40: -r{toxinidir}/examples/requirements/django_4_0.txt django40: -r{toxinidir}/examples/requirements/django_4_0.txt
django41: -r{toxinidir}/examples/requirements/django_4_1.txt
commands = commands =
# {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.test --traceback -v 3 ; {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.test --traceback -v 3
pip install -e . ; {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 ; {envpython} runtests.py
pytest {envpython} -m pytest
#[testenv:flake8] #[testenv:flake8]
#basepython = python3.5 #basepython = python3.5