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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -244,6 +244,7 @@ six==1.16.0
# via
# -r common.in
# bleach
# django-simple-captcha
# feincms
# python-dateutil
# 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
# bleach
# django-fobi
# django-simple-captcha
# feincms
# python-dateutil
# tox

View file

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

View file

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

View file

@ -3,9 +3,7 @@ import uuid
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

View file

@ -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 @@ if DEBUG and DEBUG_TOOLBAR:
pass
# Only now make proper assignments
if DJANGO_GTE_2_0:
MIDDLEWARE = _MIDDLEWARE
else:
MIDDLEWARE_CLASSES = _MIDDLEWARE
MIDDLEWARE = _MIDDLEWARE
if DEBUG:
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
__all__ = (
"urlpatterns",
)
admin.autodiscover()
# Mapping.
@ -60,14 +64,9 @@ url_patterns_args = [
),
]
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:

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_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 "$@"

View file

@ -3,7 +3,7 @@ import os
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 @@ try:
".. figure:: https://github.com/barseghyanartur/django-fobi/raw/"
"main/docs/_static",
)
except:
except OSError:
readme = ""
screenshots = ""
@ -251,6 +251,7 @@ setup(
"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 "

View file

@ -1,5 +1,5 @@
__title__ = "django-fobi"
__version__ = "0.19.5"
__version__ = "0.19.6"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2022 Artur Barseghyan"
__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.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 @@ class BasePluginModelAdmin(admin.ModelAdmin):
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 @@ admin.site.register(FormElement, FormElementAdmin)
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 @@ admin.site.register(FormHandler, FormHandlerAdmin)
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."""

View file

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

View file

@ -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 @@ from . import UID as INTEGRATE_WITH_UID
__title__ = "fobi.contrib.apps.drf_integration.dynamic"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan"
__copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = (
"assemble_serializer_class",

View file

@ -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 @@ class NullBooleanSelectPlugin(
"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
)
]

View file

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

View file

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

View file

@ -1,4 +1,3 @@
# from mezzanine.conf import settings
from mezzanine.pages.page_processors import processor_for
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"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan"
__copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("ContentImagePlugin",)

View file

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

View file

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

View file

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

View file

@ -5,7 +5,6 @@ from django.forms.utils import flatatt
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

View file

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

View file

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

View file

@ -5,7 +5,6 @@ from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
from django_nine import versions
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.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 @@ class BaseFobiBrowserBuldDynamicFormsTest(StaticLiveServerTestCase):
"{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 @@ class BaseFobiBrowserBuldDynamicFormsTest(StaticLiveServerTestCase):
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"):

View file

@ -99,7 +99,7 @@ from fobi.contrib.plugins.form_handlers.mail_sender.fobi_form_handlers import (
__title__ = "fobi.tests.data"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__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 @@ 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_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 @@ TEST_FORM_HANDLER_PLUGIN_DATA = OrderedDict(
),
(
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 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 .data import (
)
from .helpers import db_clean_up
from fobi.models import FormEntry
__title__ = "fobi.tests.test_browser_build_dynamic_forms"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan"
__copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FobiBrowserBuldDynamicFormsTest",)
@ -57,7 +53,8 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
"""
# # 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
"""
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# +++++++++++++++++++++++++++ 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# ++++++++++++++++++++++ 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
# ++++++++++++++++++++ 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 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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
@ -678,10 +806,28 @@ class FobiBrowserBuldDynamicFormsTest(BaseFobiBrowserBuldDynamicFormsTest):
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
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()

View file

@ -1,5 +1,4 @@
import datetime
import unittest
from django.test import RequestFactory, TestCase
from django.urls import reverse
@ -20,7 +19,7 @@ from fobi.models import FormEntry, FormWizardEntry
__title__ = "fobi.tests.test_core"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan"
__copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FobiCoreTest",)
@ -196,7 +195,3 @@ class FobiCoreTest(TestCase):
form_entry.active_date_from = None
form_entry.active_date_to = now
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 .core import print_info
@ -14,7 +12,7 @@ from fobi.dynamic import assemble_form_class
__title__ = "fobi.tests.test_dynamic_forms"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan"
__copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FobiDynamicFormsTest",)
@ -51,7 +49,3 @@ class FobiDynamicFormsTest(TestCase):
flow.append(rendered_form)
return flow
if __name__ == "__main__":
unittest.main()

View file

@ -1,14 +1,15 @@
import logging
import unittest
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
import factories
from selenium.webdriver.support.wait import WebDriverWait
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>"
__copyright__ = "2014-2019 Artur Barseghyan"
__copyright__ = "2014-2022 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("FeinCMSIntegrationTest",)
@ -36,7 +37,8 @@ class FeinCMSIntegrationTest(BaseFobiBrowserBuldDynamicFormsTest):
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 @@ class FeinCMSIntegrationTest(BaseFobiBrowserBuldDynamicFormsTest):
# 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()

View file

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

View file

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

View file

@ -48,7 +48,7 @@ from .settings import (
__title__ = "fobi.utils"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__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",

View file

@ -19,7 +19,6 @@ from django.template import RequestContext
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

View file

@ -10,7 +10,6 @@ from django.urls import reverse
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

12
tox.ini
View file

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