mirror of
https://github.com/Crocmagnon/checkout.git
synced 2024-11-21 23:58:02 +01:00
Add functional tests
This commit is contained in:
parent
30774d0dca
commit
ecf3c4dce2
7 changed files with 680 additions and 9 deletions
287
poetry.lock
generated
287
poetry.lock
generated
|
@ -17,6 +17,22 @@ python-versions = ">=3.7"
|
|||
[package.extras]
|
||||
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
|
||||
|
||||
[[package]]
|
||||
name = "async-generator"
|
||||
version = "1.10"
|
||||
description = "Async generators and context managers for Python 3.5+"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.1"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "22.1.0"
|
||||
|
@ -97,6 +113,17 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "1.15.1"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
pycparser = "*"
|
||||
|
||||
[[package]]
|
||||
name = "cfgv"
|
||||
version = "3.3.1"
|
||||
|
@ -296,6 +323,32 @@ python-versions = ">=3.6"
|
|||
[package.dependencies]
|
||||
Django = ">=3.2"
|
||||
|
||||
[[package]]
|
||||
name = "factory-boy"
|
||||
version = "3.2.1"
|
||||
description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
Faker = ">=0.7.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage", "django", "flake8", "isort", "pillow", "sqlalchemy", "mongoengine", "wheel (>=0.32.0)", "tox", "zest.releaser"]
|
||||
doc = ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"]
|
||||
|
||||
[[package]]
|
||||
name = "faker"
|
||||
version = "14.2.1"
|
||||
description = "Faker is a Python package that generates fake data for you."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
python-dateutil = ">=2.4"
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.8.0"
|
||||
|
@ -366,6 +419,14 @@ gevent = ["gevent (>=1.4.0)"]
|
|||
setproctitle = ["setproctitle"]
|
||||
tornado = ["tornado (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.13.0"
|
||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.5.5"
|
||||
|
@ -467,6 +528,17 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
|
||||
[[package]]
|
||||
name = "outcome"
|
||||
version = "1.2.0"
|
||||
description = "Capture the outcome of Python function calls."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=19.2.0"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.3"
|
||||
|
@ -567,6 +639,14 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.21"
|
||||
description = "C parser in Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.13.0"
|
||||
|
@ -602,25 +682,46 @@ beautifulsoup4 = ">=4.5,<5.0"
|
|||
packaging = ">=20"
|
||||
requests = ">=2.20,<3.0"
|
||||
|
||||
[[package]]
|
||||
name = "pysocks"
|
||||
version = "1.7.1"
|
||||
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.1.3"
|
||||
version = "6.2.5"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
py = ">=1.8.2"
|
||||
tomli = ">=1.0.0"
|
||||
toml = "*"
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-base-url"
|
||||
version = "2.0.0"
|
||||
description = "pytest plugin for URL based testing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=3.0.0,<8.0.0"
|
||||
requests = ">=2.9"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
|
@ -652,6 +753,65 @@ pytest = ">=5.4.0"
|
|||
docs = ["sphinx", "sphinx-rtd-theme"]
|
||||
testing = ["django", "django-configurations (>=2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-html"
|
||||
version = "3.1.1"
|
||||
description = "pytest plugin for generating HTML reports"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=5.0,<6.0.0 || >6.0.0"
|
||||
pytest-metadata = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-metadata"
|
||||
version = "2.0.2"
|
||||
description = "pytest plugin for test session metadata"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=3.0.0,<8.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-selenium"
|
||||
version = "4.0.0"
|
||||
description = "pytest plugin for Selenium"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=6.0.0,<7.0.0"
|
||||
pytest-base-url = ">=2.0.0,<3.0.0"
|
||||
pytest-html = ">=2.0.0"
|
||||
pytest-variables = ">=2.0.0,<3.0.0"
|
||||
requests = ">=2.26.0,<3.0.0"
|
||||
selenium = ">=4.0.0,<5.0.0"
|
||||
tenacity = ">=6.0.0,<7.0.0"
|
||||
|
||||
[package.extras]
|
||||
appium = ["appium-python-client (>=2.0.0,<3.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-variables"
|
||||
version = "2.0.0"
|
||||
description = "pytest plugin for providing variables to tests/fixtures"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=3.0.0,<8.0.0"
|
||||
|
||||
[package.extras]
|
||||
toml = ["toml"]
|
||||
yaml = ["pyyaml"]
|
||||
hjson = ["hjson"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.8.2"
|
||||
|
@ -724,6 +884,20 @@ python-versions = "*"
|
|||
[package.dependencies]
|
||||
requests = ">=2.0.1,<3.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "selenium"
|
||||
version = "4.4.3"
|
||||
description = ""
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "~=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2021.10.8"
|
||||
trio = ">=0.17,<1.0"
|
||||
trio-websocket = ">=0.9,<1.0"
|
||||
urllib3 = {version = ">=1.26,<2.0", extras = ["socks"]}
|
||||
|
||||
[[package]]
|
||||
name = "setuptools-scm"
|
||||
version = "7.0.5"
|
||||
|
@ -749,6 +923,22 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.0"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "sortedcontainers"
|
||||
version = "2.4.0"
|
||||
description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.3.2.post1"
|
||||
|
@ -765,6 +955,20 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "tenacity"
|
||||
version = "6.3.1"
|
||||
description = "Retry code until it succeeds"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
six = ">=1.9.0"
|
||||
|
||||
[package.extras]
|
||||
doc = ["reno", "sphinx", "tornado (>=4.5)"]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
|
@ -781,6 +985,36 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "trio"
|
||||
version = "0.21.0"
|
||||
description = "A friendly Python library for async concurrency and I/O"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
async-generator = ">=1.9"
|
||||
attrs = ">=19.2.0"
|
||||
cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""}
|
||||
idna = "*"
|
||||
outcome = "*"
|
||||
sniffio = "*"
|
||||
sortedcontainers = "*"
|
||||
|
||||
[[package]]
|
||||
name = "trio-websocket"
|
||||
version = "0.9.2"
|
||||
description = "WebSocket library for Trio"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.dependencies]
|
||||
async-generator = ">=1.10"
|
||||
trio = ">=0.11"
|
||||
wsproto = ">=0.14"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.3.0"
|
||||
|
@ -805,6 +1039,9 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
|
||||
|
||||
[package.dependencies]
|
||||
PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""}
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"]
|
||||
|
@ -849,10 +1086,21 @@ Brotli = {version = "*", optional = true, markers = "extra == \"brotli\""}
|
|||
[package.extras]
|
||||
brotli = ["brotli"]
|
||||
|
||||
[[package]]
|
||||
name = "wsproto"
|
||||
version = "1.2.0"
|
||||
description = "WebSockets state-machine based protocol implementation"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
|
||||
[package.dependencies]
|
||||
h11 = ">=0.9.0,<1"
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "b0fdaacb7c4e3ecc472664d3091567919a34505639c79e6ecfe8f58a00020566"
|
||||
content-hash = "a7f456302a8e5cd5023a9f6ced2930e9ec4a2e964daae04f24a486b2071fd3a0"
|
||||
|
||||
[metadata.files]
|
||||
ansicon = []
|
||||
|
@ -860,6 +1108,8 @@ asgiref = [
|
|||
{file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"},
|
||||
{file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"},
|
||||
]
|
||||
async-generator = []
|
||||
atomicwrites = []
|
||||
attrs = []
|
||||
beautifulsoup4 = [
|
||||
{file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"},
|
||||
|
@ -932,6 +1182,7 @@ brotli = [
|
|||
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
|
||||
]
|
||||
certifi = []
|
||||
cffi = []
|
||||
cfgv = [
|
||||
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
|
||||
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
|
||||
|
@ -970,6 +1221,8 @@ django-environ = [
|
|||
{file = "django_environ-0.9.0-py2.py3-none-any.whl", hash = "sha256:f21a5ef8cc603da1870bbf9a09b7e5577ab5f6da451b843dbcc721a7bca6b3d9"},
|
||||
]
|
||||
django-extensions = []
|
||||
factory-boy = []
|
||||
faker = []
|
||||
filelock = []
|
||||
fonttools = []
|
||||
freezegun = []
|
||||
|
@ -978,6 +1231,7 @@ gunicorn = [
|
|||
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
|
||||
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
|
||||
]
|
||||
h11 = []
|
||||
identify = []
|
||||
idna = []
|
||||
iniconfig = [
|
||||
|
@ -994,6 +1248,7 @@ matplotlib = []
|
|||
model-bakery = []
|
||||
nodeenv = []
|
||||
numpy = []
|
||||
outcome = []
|
||||
packaging = [
|
||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||
|
@ -1014,13 +1269,19 @@ py = [
|
|||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pycparser = []
|
||||
pygments = []
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||
]
|
||||
pypi-simple = []
|
||||
pytest = []
|
||||
pysocks = []
|
||||
pytest = [
|
||||
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
|
||||
{file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
|
||||
]
|
||||
pytest-base-url = []
|
||||
pytest-cov = [
|
||||
{file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
|
||||
{file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
|
||||
|
@ -1029,6 +1290,13 @@ pytest-django = [
|
|||
{file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"},
|
||||
{file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"},
|
||||
]
|
||||
pytest-html = [
|
||||
{file = "pytest-html-3.1.1.tar.gz", hash = "sha256:3ee1cf319c913d19fe53aeb0bc400e7b0bc2dbeb477553733db1dad12eb75ee3"},
|
||||
{file = "pytest_html-3.1.1-py3-none-any.whl", hash = "sha256:b7f82f123936a3f4d2950bc993c2c1ca09ce262c9ae12f9ac763a2401380b455"},
|
||||
]
|
||||
pytest-metadata = []
|
||||
pytest-selenium = []
|
||||
pytest-variables = []
|
||||
python-dateutil = [
|
||||
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
||||
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
||||
|
@ -1075,16 +1343,20 @@ requests-toolbelt = [
|
|||
{file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"},
|
||||
{file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"},
|
||||
]
|
||||
selenium = []
|
||||
setuptools-scm = []
|
||||
six = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
sniffio = []
|
||||
sortedcontainers = []
|
||||
soupsieve = [
|
||||
{file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
|
||||
{file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
|
||||
]
|
||||
sqlparse = []
|
||||
tenacity = []
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
|
@ -1093,6 +1365,8 @@ tomli = [
|
|||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
trio = []
|
||||
trio-websocket = []
|
||||
typing-extensions = []
|
||||
tzdata = []
|
||||
urllib3 = []
|
||||
|
@ -1105,3 +1379,4 @@ whitenoise = [
|
|||
{file = "whitenoise-6.2.0-py3-none-any.whl", hash = "sha256:8e9c600a5c18bd17655ef668ad55b5edf6c24ce9bdca5bf607649ca4b1e8e2c2"},
|
||||
{file = "whitenoise-6.2.0.tar.gz", hash = "sha256:8fa943c6d4cd9e27673b70c21a07b0aa120873901e099cd46cab40f7cc96d567"},
|
||||
]
|
||||
wsproto = []
|
||||
|
|
|
@ -24,12 +24,15 @@ freezegun = "^1.2.1"
|
|||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pre-commit = "^2.7"
|
||||
pytest = "^7.1"
|
||||
pytest = "^6.0"
|
||||
pytest-django = "^4.5"
|
||||
model-bakery = "^1.1"
|
||||
pytest-cov = "^3.0"
|
||||
poetry-deps-scanner = "^2.0"
|
||||
invoke = "^1.7.0"
|
||||
factory-boy = "^3.2.1"
|
||||
pytest-selenium = "^4.0.0"
|
||||
selenium = "^4.4.3"
|
||||
|
||||
[tool.black]
|
||||
target-version = ['py310']
|
||||
|
@ -38,7 +41,7 @@ target-version = ['py310']
|
|||
profile = "black"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "--color=yes"
|
||||
addopts = "--color=yes --driver Firefox"
|
||||
minversion = "6.0"
|
||||
DJANGO_SETTINGS_MODULE = "checkout.settings"
|
||||
testpaths = [
|
||||
|
|
19
src/conftest.py
Normal file
19
src/conftest.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
import pytest
|
||||
from django.core.management import call_command
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def _collectstatic():
|
||||
call_command("collectstatic", interactive=False, verbosity=0)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def live_server(settings, live_server):
|
||||
settings.STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
|
||||
return live_server
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def selenium(selenium):
|
||||
selenium.implicitly_wait(3)
|
||||
return selenium
|
|
@ -1 +0,0 @@
|
|||
# Create your tests here.
|
0
src/purchase/tests/__init__.py
Normal file
0
src/purchase/tests/__init__.py
Normal file
84
src/purchase/tests/factories.py
Normal file
84
src/purchase/tests/factories.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
import random
|
||||
from functools import partial
|
||||
|
||||
import factory
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
|
||||
from common.models import User
|
||||
from purchase.models import Basket, BasketItem, PaymentMethod, Product
|
||||
|
||||
USER_PASSWORD = "test_password"
|
||||
|
||||
|
||||
class CashierFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = User
|
||||
|
||||
username = factory.Faker("user_name")
|
||||
password = make_password(USER_PASSWORD)
|
||||
is_active = True
|
||||
is_staff = True
|
||||
|
||||
@factory.post_generation
|
||||
def groups(self, create, extracted, **kwargs):
|
||||
if create:
|
||||
self.groups.add(CashierGroupFactory())
|
||||
|
||||
|
||||
class ProductFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Product
|
||||
|
||||
name = factory.Faker("text", max_nb_chars=80)
|
||||
unit_price_cents = factory.LazyFunction(partial(random.randint, 80, 650))
|
||||
|
||||
|
||||
class PaymentMethodFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = PaymentMethod
|
||||
|
||||
name = factory.Faker("text", max_nb_chars=30)
|
||||
|
||||
|
||||
class BasketWithItemsFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Basket
|
||||
|
||||
payment_method = factory.Iterator(PaymentMethod.objects.all())
|
||||
|
||||
@factory.post_generation
|
||||
def items(self, create, extracted, **kwargs):
|
||||
if create:
|
||||
products = Product.objects.order_by("?")
|
||||
quantity = random.randint(1, len(products))
|
||||
for product in products[:quantity]:
|
||||
BasketItem.objects.create(
|
||||
product=product,
|
||||
basket=self,
|
||||
quantity=random.randint(1, 4),
|
||||
unit_price_cents=product.unit_price_cents,
|
||||
)
|
||||
|
||||
|
||||
class CashierGroupFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Group
|
||||
|
||||
name = "Caissier"
|
||||
|
||||
@factory.post_generation
|
||||
def permissions(self, create, extracted, **kwargs):
|
||||
if create:
|
||||
self.permissions.add(
|
||||
Permission.objects.get(codename="add_basket"),
|
||||
Permission.objects.get(codename="change_basket"),
|
||||
Permission.objects.get(codename="delete_basket"),
|
||||
Permission.objects.get(codename="view_basket"),
|
||||
Permission.objects.get(codename="add_basketitem"),
|
||||
Permission.objects.get(codename="change_basketitem"),
|
||||
Permission.objects.get(codename="delete_basketitem"),
|
||||
Permission.objects.get(codename="view_basketitem"),
|
||||
Permission.objects.get(codename="view_paymentmethod"),
|
||||
Permission.objects.get(codename="view_product"),
|
||||
)
|
291
src/purchase/tests/test_cashier_flow.py
Normal file
291
src/purchase/tests/test_cashier_flow.py
Normal file
|
@ -0,0 +1,291 @@
|
|||
import time
|
||||
|
||||
import freezegun
|
||||
from django.urls import reverse
|
||||
from pytest_django.live_server_helper import LiveServer
|
||||
from selenium.webdriver import ActionChains, Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.remote.webdriver import WebDriver
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
from common.models import User
|
||||
from purchase.models import Basket
|
||||
from purchase.tests.factories import (
|
||||
USER_PASSWORD,
|
||||
BasketWithItemsFactory,
|
||||
CashierFactory,
|
||||
PaymentMethodFactory,
|
||||
ProductFactory,
|
||||
)
|
||||
|
||||
|
||||
@freezegun.freeze_time("2022-09-24 19:01:00+0200")
|
||||
def test_cashier_create_and_update_basket(live_server: LiveServer, selenium: WebDriver):
|
||||
wait = WebDriverWait(selenium, 10)
|
||||
assert Basket.objects.count() == 0
|
||||
|
||||
# Setup data
|
||||
cashier = CashierFactory()
|
||||
products = [
|
||||
ProductFactory(),
|
||||
ProductFactory(),
|
||||
ProductFactory(),
|
||||
]
|
||||
payment_methods = [
|
||||
PaymentMethodFactory(),
|
||||
PaymentMethodFactory(),
|
||||
PaymentMethodFactory(),
|
||||
]
|
||||
|
||||
login(live_server, selenium, cashier)
|
||||
|
||||
# Assert products are displayed
|
||||
redirect_url = live_reverse(live_server, "purchase:new")
|
||||
wait.until(lambda driver: driver.current_url == redirect_url)
|
||||
displayed_products = selenium.find_elements(By.CSS_SELECTOR, ".card.h-100")
|
||||
assert len(displayed_products) == len(products)
|
||||
for product, displayed_product in zip(products, displayed_products):
|
||||
assert (
|
||||
product.name
|
||||
== displayed_product.find_element(By.CLASS_NAME, "card-title").text
|
||||
)
|
||||
|
||||
# Assert quantity of all products is 0
|
||||
for displayed_product in displayed_products:
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
quantity = int(quantity_input.get_attribute("value"))
|
||||
assert quantity == 0
|
||||
|
||||
# Click on - on product 1
|
||||
displayed_product = displayed_products[0]
|
||||
displayed_product.find_element(By.CLASS_NAME, "btn-danger").click()
|
||||
|
||||
# Assert quantity is still 0
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
quantity = int(quantity_input.get_attribute("value"))
|
||||
assert quantity == 0
|
||||
|
||||
# Click two times on + on product 1
|
||||
button_plus = displayed_product.find_element(By.CLASS_NAME, "btn-success")
|
||||
button_plus.click()
|
||||
button_plus.click()
|
||||
|
||||
# Assert quantity is 2
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
quantity = int(quantity_input.get_attribute("value"))
|
||||
assert quantity == 2
|
||||
|
||||
# Adjust manually quantity for product 2: 4
|
||||
displayed_product = displayed_products[1]
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
chain = ActionChains(selenium)
|
||||
chain.double_click(quantity_input).perform()
|
||||
quantity_input.send_keys("4")
|
||||
|
||||
# Don't add payment method
|
||||
# Save
|
||||
selenium.find_element(By.ID, "submit-id-submit").click()
|
||||
|
||||
# Assert entries saved in DB (new basket with proper products)
|
||||
assert Basket.objects.count() == 1
|
||||
basket = Basket.objects.priced().first()
|
||||
assert basket.payment_method is None
|
||||
assert basket.items.count() == 2
|
||||
assert basket.items.get(product=products[0]).quantity == 2
|
||||
assert (
|
||||
basket.items.get(product=products[0]).unit_price_cents
|
||||
== products[0].unit_price_cents
|
||||
)
|
||||
assert basket.items.get(product=products[1]).quantity == 4
|
||||
assert (
|
||||
basket.items.get(product=products[1]).unit_price_cents
|
||||
== products[1].unit_price_cents
|
||||
)
|
||||
|
||||
# Assert redirected to basket update view
|
||||
redirect_url = live_reverse(live_server, "purchase:update", pk=basket.pk)
|
||||
wait.until(lambda driver: driver.current_url == redirect_url)
|
||||
|
||||
# Assert message in green for successful basket creation
|
||||
created_message = selenium.find_element(By.CSS_SELECTOR, ".messages .alert-success")
|
||||
assert created_message.text == "Panier correctement créé."
|
||||
|
||||
# Assert message in red for missing payment method
|
||||
missing_payment = selenium.find_element(By.CSS_SELECTOR, ".alert.alert-danger")
|
||||
assert missing_payment.text == "Moyen de paiement manquant."
|
||||
|
||||
# Assert ID, price, date & product quantities
|
||||
# Selected products have a green background
|
||||
title = selenium.find_element(By.TAG_NAME, "h1")
|
||||
assert title.text == f"Panier n°{basket.pk} {basket.price/100:.2f}€"
|
||||
date = selenium.find_element(By.CLASS_NAME, "metadata")
|
||||
assert date.text == "24 septembre 2022 19:01"
|
||||
|
||||
displayed_products = selenium.find_elements(By.CSS_SELECTOR, ".card.h-100")
|
||||
displayed_product = displayed_products[0]
|
||||
assert "bg-success" in displayed_product.get_attribute("class")
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
quantity = int(quantity_input.get_attribute("value"))
|
||||
assert quantity == 2
|
||||
displayed_product = displayed_products[1]
|
||||
assert "bg-success" in displayed_product.get_attribute("class")
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
quantity = int(quantity_input.get_attribute("value"))
|
||||
assert quantity == 4
|
||||
displayed_product = displayed_products[2]
|
||||
assert "bg-success" not in displayed_product.get_attribute("class")
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
quantity = int(quantity_input.get_attribute("value"))
|
||||
assert quantity == 0
|
||||
|
||||
# Click on - on product 2
|
||||
displayed_product = displayed_products[1]
|
||||
displayed_product.find_element(By.CLASS_NAME, "btn-danger").click()
|
||||
|
||||
# Assert quantity is 3
|
||||
quantity_input = displayed_product.find_element(By.CLASS_NAME, "numberinput")
|
||||
quantity = int(quantity_input.get_attribute("value"))
|
||||
assert quantity == 3
|
||||
|
||||
# Add payment method
|
||||
selenium.find_element(By.TAG_NAME, "html").send_keys(Keys.END)
|
||||
time.sleep(1)
|
||||
selenium.find_element(By.ID, f"id_payment_method_{payment_methods[1].pk}").click()
|
||||
|
||||
# Save
|
||||
selenium.find_element(By.ID, "submit-id-submit").click()
|
||||
|
||||
# Assert changed in DB
|
||||
assert Basket.objects.count() == 1
|
||||
basket = Basket.objects.priced().first()
|
||||
assert basket.payment_method == payment_methods[1]
|
||||
assert basket.items.count() == 2
|
||||
assert basket.items.get(product=products[0]).quantity == 2
|
||||
assert (
|
||||
basket.items.get(product=products[0]).unit_price_cents
|
||||
== products[0].unit_price_cents
|
||||
)
|
||||
assert basket.items.get(product=products[1]).quantity == 3
|
||||
assert (
|
||||
basket.items.get(product=products[1]).unit_price_cents
|
||||
== products[1].unit_price_cents
|
||||
)
|
||||
|
||||
# Assert redirected to same view
|
||||
redirect_url = live_reverse(live_server, "purchase:update", pk=basket.pk)
|
||||
wait.until(lambda driver: driver.current_url == redirect_url)
|
||||
|
||||
# Assert message in green for successful basket update
|
||||
created_message = selenium.find_element(By.CSS_SELECTOR, ".messages .alert-success")
|
||||
assert created_message.text == "Panier correctement modifié."
|
||||
|
||||
# Assert no more red message
|
||||
missing_payment = selenium.find_elements(By.CSS_SELECTOR, ".alert.alert-danger")
|
||||
assert len(missing_payment) == 0
|
||||
|
||||
|
||||
def login(
|
||||
live_server: LiveServer, selenium: WebDriver, cashier: User, url: str = "/"
|
||||
) -> None:
|
||||
# Go to page
|
||||
url = live_url(live_server, url)
|
||||
selenium.get(url)
|
||||
# Login
|
||||
selenium.find_element(By.ID, "id_username").send_keys(cashier.username)
|
||||
selenium.find_element(By.ID, "id_password").send_keys(USER_PASSWORD)
|
||||
selenium.find_element(By.ID, "id_password").send_keys(Keys.RETURN)
|
||||
|
||||
|
||||
@freezegun.freeze_time("2022-09-24 19:03:00+0200")
|
||||
def test_baskets_list(live_server: LiveServer, selenium: WebDriver):
|
||||
wait = WebDriverWait(selenium, 10)
|
||||
|
||||
# Setup test data
|
||||
cashier = CashierFactory()
|
||||
_ = [
|
||||
ProductFactory(),
|
||||
ProductFactory(),
|
||||
ProductFactory(),
|
||||
]
|
||||
_ = [
|
||||
PaymentMethodFactory(),
|
||||
PaymentMethodFactory(),
|
||||
PaymentMethodFactory(),
|
||||
]
|
||||
with freezegun.freeze_time("2022-09-24 19:01:00+0200"):
|
||||
basket_with_payment_method = BasketWithItemsFactory()
|
||||
basket_with_payment_method = Basket.objects.priced().get(
|
||||
pk=basket_with_payment_method.pk
|
||||
)
|
||||
with freezegun.freeze_time("2022-09-24 19:02:00+0200"):
|
||||
basket_no_payment_method = BasketWithItemsFactory(payment_method=None)
|
||||
basket_no_payment_method = Basket.objects.priced().get(
|
||||
pk=basket_no_payment_method.pk
|
||||
)
|
||||
|
||||
# Login
|
||||
url = reverse("purchase:list")
|
||||
login(live_server, selenium, cashier, url)
|
||||
|
||||
# Assert first basket (last created) has yellow background
|
||||
# Assert basket info displayed
|
||||
displayed_baskets = selenium.find_elements(By.CSS_SELECTOR, ".card.h-100")
|
||||
first_basket = displayed_baskets[0]
|
||||
assert "bg-warning" in first_basket.get_attribute("class")
|
||||
text = first_basket.text.replace("\n", " ")
|
||||
assert f"n°{basket_no_payment_method.pk} " in text
|
||||
expected_articles_count = basket_no_payment_method.items.count()
|
||||
assert f" {expected_articles_count} article" in text
|
||||
expected_price = basket_no_payment_method.price / 100
|
||||
assert f" {expected_price:.2f}€" in text
|
||||
expected_payment_method = "-"
|
||||
assert f" {expected_payment_method} " in text
|
||||
assert "19:02" in text
|
||||
|
||||
# Assert second basket (first created) doesn't have yellow background
|
||||
# Assert basket info displayed including payment method
|
||||
second_basket = displayed_baskets[1]
|
||||
assert "bg-warning" not in second_basket.get_attribute("class")
|
||||
text = second_basket.text.replace("\n", " ")
|
||||
assert f"n°{basket_with_payment_method.pk} " in text
|
||||
expected_articles_count = basket_with_payment_method.items.count()
|
||||
assert f" {expected_articles_count} article" in text
|
||||
expected_price = basket_with_payment_method.price / 100
|
||||
assert f" {expected_price:.2f}€" in text
|
||||
expected_payment_method = basket_with_payment_method.payment_method.name
|
||||
assert f" {expected_payment_method} " in text
|
||||
assert "19:01" in text
|
||||
|
||||
# Click on delete on second basket
|
||||
second_basket.find_element(By.CLASS_NAME, "btn-danger").click()
|
||||
|
||||
# Confirm deletion
|
||||
selenium.find_element(By.CLASS_NAME, "btn-danger").click()
|
||||
|
||||
# Assert object deleted in DB
|
||||
assert Basket.objects.count() == 1
|
||||
assert Basket.objects.first() == basket_no_payment_method
|
||||
|
||||
# Assert redirected to list view
|
||||
wait.until(
|
||||
lambda driver: driver.current_url == live_reverse(live_server, "purchase:list")
|
||||
)
|
||||
|
||||
# Click on edit on remaining basket
|
||||
displayed_baskets = selenium.find_elements(By.CSS_SELECTOR, ".card.h-100")
|
||||
displayed_baskets[0].find_element(By.CLASS_NAME, "btn-primary").click()
|
||||
|
||||
# Assert redirected to edit view
|
||||
redirect_url = live_reverse(
|
||||
live_server, "purchase:update", pk=basket_no_payment_method.pk
|
||||
)
|
||||
wait.until(lambda driver: driver.current_url == redirect_url)
|
||||
|
||||
|
||||
def live_reverse(live_server: LiveServer, url_name: str, **kwargs) -> str:
|
||||
path = reverse(url_name, kwargs=kwargs)
|
||||
return live_url(live_server, path)
|
||||
|
||||
|
||||
def live_url(live_server: LiveServer, path: str) -> str:
|
||||
return live_server.url + path
|
Loading…
Reference in a new issue