wbcore 2.2.3__py2.py3-none-any.whl → 2.2.5__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wbcore/contrib/color/admin.py +28 -0
- wbcore/contrib/color/apps.py +5 -0
- wbcore/contrib/color/enums.py +17 -0
- wbcore/contrib/color/factories.py +10 -0
- wbcore/contrib/color/fields.py +29 -0
- wbcore/contrib/color/forms.py +13 -0
- wbcore/contrib/color/models.py +62 -0
- wbcore/contrib/color/tests/conftest.py +10 -0
- wbcore/contrib/color/tests/test_color_models.py +61 -0
- wbcore/contrib/color/tests/test_fields.py +25 -0
- wbcore/contrib/documents/tests/conftest.py +30 -0
- wbcore/contrib/documents/tests/test_models.py +144 -0
- wbcore/contrib/example_app/tests/test_models/test_event.py +87 -0
- wbcore/contrib/example_app/tests/test_models/test_match.py +210 -0
- wbcore/contrib/example_app/tests/test_models/test_others.py +159 -0
- wbcore/contrib/example_app/tests/test_serializers/test_league_serializer.py +34 -0
- wbcore/contrib/example_app/tests/test_serializers/test_match_serializer.py +134 -0
- wbcore/contrib/example_app/tests/test_serializers/test_role_serializer.py +13 -0
- wbcore/contrib/example_app/tests/test_serializers/test_sport_serializer.py +14 -0
- wbcore/contrib/example_app/tests/test_serializers/test_stadium_serializer.py +14 -0
- wbcore/contrib/example_app/tests/test_serializers/test_team_result_serializer.py +30 -0
- wbcore/contrib/example_app/tests/test_serializers/test_team_serializer.py +70 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_event_viewset.py +162 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_league_viewset.py +84 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_match_viewset.py +65 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_person_viewset.py +166 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_role_viewset.py +75 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_sport_viewset.py +75 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_stadium_viewset.py +75 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_team_viewset.py +92 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_teamresult_viewset.py +58 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_utils_viewsets.py +124 -0
- wbcore/contrib/guardian/apps.py +6 -0
- wbcore/contrib/guardian/configurations.py +3 -0
- wbcore/contrib/guardian/filters.py +21 -0
- wbcore/contrib/guardian/tasks.py +10 -0
- wbcore/contrib/guardian/urls.py +12 -0
- wbcore/contrib/guardian/utils.py +124 -0
- wbcore/contrib/notifications/viewsets/configs/notification_types.py +27 -0
- wbcore/contrib/notifications/viewsets/configs/notifications.py +85 -0
- wbcore/contrib/workflow/tests/test_models/step/test_decision_step.py +79 -0
- wbcore/contrib/workflow/tests/test_models/step/test_email_step.py +45 -0
- wbcore/contrib/workflow/tests/test_models/step/test_finish_step.py +105 -0
- wbcore/contrib/workflow/tests/test_models/step/test_join_step.py +127 -0
- wbcore/contrib/workflow/tests/test_models/step/test_script_step.py +24 -0
- wbcore/contrib/workflow/tests/test_models/step/test_split_step.py +49 -0
- wbcore/contrib/workflow/tests/test_models/step/test_step.py +621 -0
- wbcore/contrib/workflow/tests/test_models/step/test_user_step.py +225 -0
- wbcore/contrib/workflow/tests/test_models/test_condition.py +103 -0
- wbcore/contrib/workflow/tests/test_models/test_data.py +134 -0
- wbcore/contrib/workflow/tests/test_models/test_process.py +98 -0
- wbcore/contrib/workflow/tests/test_models/test_transition.py +128 -0
- wbcore/contrib/workflow/tests/test_models/test_workflow.py +358 -0
- wbcore/templates/forms.py +0 -0
- wbcore/test/e2e_helpers_methods/e2e_checks.py +121 -0
- wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +395 -0
- wbcore/tests/test_permissions/test_backend.py +29 -0
- {wbcore-2.2.3.dist-info → wbcore-2.2.5.dist-info}/METADATA +1 -1
- {wbcore-2.2.3.dist-info → wbcore-2.2.5.dist-info}/RECORD +60 -16
- wbcore/contrib/agenda/release_notes/1_0_0.md +0 -13
- wbcore/contrib/authentication/release_notes/1_0_0.md +0 -13
- wbcore/contrib/currency/release_notes/1_0_0.md +0 -13
- wbcore/contrib/directory/release_notes/1_0_0.md +0 -13
- wbcore/contrib/directory/release_notes/1_0_1.md +0 -13
- wbcore/contrib/documents/release_notes/1_0_0.md +0 -13
- wbcore/contrib/geography/release_notes/1_0_0.md +0 -13
- wbcore/contrib/io/release_notes/1_0_0.md +0 -13
- wbcore/contrib/notifications/release_notes/1_0_0.md +0 -13
- wbcore/contrib/tags/release_notes/1_0_0.md +0 -13
- wbcore/docs/orderable.md +0 -29
- wbcore/docs/reparent.md +0 -13
- wbcore/templates/reversion/compare_detail.html +0 -19
- {wbcore-2.2.3.dist-info → wbcore-2.2.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib import admin
|
|
3
|
+
from django_better_admin_arrayfield.models.fields import ArrayField
|
|
4
|
+
from wbcore.contrib.color.forms import DynamicArrayColorWidget
|
|
5
|
+
from wbcore.contrib.color.models import ColorGradient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@admin.register(ColorGradient)
|
|
9
|
+
class ColorGradientModelAdmin(admin.ModelAdmin):
|
|
10
|
+
class Media:
|
|
11
|
+
if settings.DEBUG:
|
|
12
|
+
js = [
|
|
13
|
+
"colorfield/jscolor/jscolor.js",
|
|
14
|
+
"colorfield/colorfield.js",
|
|
15
|
+
"js/django_better_admin_arrayfield.min.js",
|
|
16
|
+
]
|
|
17
|
+
else:
|
|
18
|
+
js = [
|
|
19
|
+
"colorfield/jscolor/jscolor.min.js",
|
|
20
|
+
"colorfield/colorfield.js",
|
|
21
|
+
"js/django_better_admin_arrayfield.min.js",
|
|
22
|
+
]
|
|
23
|
+
css = {"all": ("css/django_better_admin_arrayfield.min.css",)}
|
|
24
|
+
|
|
25
|
+
formfield_overrides = {
|
|
26
|
+
ArrayField: {"widget": DynamicArrayColorWidget},
|
|
27
|
+
}
|
|
28
|
+
list_display = ["title"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WBColor(Enum):
|
|
5
|
+
GREEN_LIGHT = "rgb(214, 229, 145)"
|
|
6
|
+
GREEN = "rgb(182, 223, 18)"
|
|
7
|
+
GREEN_DARK = "rgb(111, 181, 68)"
|
|
8
|
+
RED_LIGHT = "rgb(244, 132, 116)"
|
|
9
|
+
RED = "rgb(250, 84, 62)"
|
|
10
|
+
RED_DARK = "rgb(224, 57, 49)"
|
|
11
|
+
YELLOW_LIGHT = "rgb(247, 232, 113)"
|
|
12
|
+
YELLOW = "rgb(247, 207, 77)"
|
|
13
|
+
YELLOW_DARK = "rgb(247, 190, 47)"
|
|
14
|
+
GREY = "rgb(133, 144, 162)"
|
|
15
|
+
BLUE_LIGHT = "rgb(104, 198, 224)"
|
|
16
|
+
BLUE = "rgb(69, 112, 224)"
|
|
17
|
+
BLUE_DARK = "rgb(33, 26, 233)"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import factory
|
|
2
|
+
from wbcore.contrib.color.models import ColorGradient
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ColorGradientFactory(factory.django.DjangoModelFactory):
|
|
6
|
+
class Meta:
|
|
7
|
+
model = ColorGradient
|
|
8
|
+
|
|
9
|
+
title = factory.Sequence(lambda n: f"Color Gradient {n}")
|
|
10
|
+
colors = factory.List([factory.Faker("color") for _ in range(10)])
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from django.core.exceptions import ValidationError
|
|
4
|
+
from django.db.models import CharField
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def validate_color(color_string):
|
|
8
|
+
"""Hexadecimal validation."""
|
|
9
|
+
if color_string is None:
|
|
10
|
+
return color_string
|
|
11
|
+
|
|
12
|
+
if not re.search(r"^#(?:[0-9a-fA-F]{3}){1,2}$", color_string):
|
|
13
|
+
raise ValidationError("Please provide a valid hexadecimal color.")
|
|
14
|
+
|
|
15
|
+
return color_string
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ColorField(CharField):
|
|
19
|
+
description = "A field storing a hexadecimal color value"
|
|
20
|
+
default_validators = [validate_color]
|
|
21
|
+
|
|
22
|
+
def __init__(self, *args, **kwargs):
|
|
23
|
+
kwargs["max_length"] = 7
|
|
24
|
+
super().__init__(*args, **kwargs)
|
|
25
|
+
|
|
26
|
+
def deconstruct(self):
|
|
27
|
+
name, path, args, kwargs = super().deconstruct()
|
|
28
|
+
del kwargs["max_length"]
|
|
29
|
+
return name, path, args, kwargs
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DynamicArrayColorWidget(DynamicArrayWidget):
|
|
5
|
+
template_name = "wbcore/dynamic_color_array.html"
|
|
6
|
+
|
|
7
|
+
def get_context(self, name, value, attrs):
|
|
8
|
+
context = super().get_context(name, value, attrs)
|
|
9
|
+
context["default"] = "#ffffff"
|
|
10
|
+
return context
|
|
11
|
+
|
|
12
|
+
def __init__(self, *args, **kwargs):
|
|
13
|
+
super().__init__(*args, **kwargs)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Generator
|
|
2
|
+
|
|
3
|
+
from colorfield.fields import ColorField
|
|
4
|
+
from django.db import models
|
|
5
|
+
from django_better_admin_arrayfield.models.fields import ArrayField
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ColorGradient(models.Model):
|
|
9
|
+
"""
|
|
10
|
+
Utility class for color gradient support in report
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
title = models.CharField(max_length=128, default="")
|
|
14
|
+
colors = ArrayField(ColorField(default="#FF0000"), default=list)
|
|
15
|
+
|
|
16
|
+
def __str__(self) -> str:
|
|
17
|
+
return self.title
|
|
18
|
+
|
|
19
|
+
def get_gradient(self, color: str | None = None, color_size: int = -1) -> Generator:
|
|
20
|
+
"""
|
|
21
|
+
Return the consecutive colors (gradient) given a color based on the closest distance
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
color: a hex color. Optional.
|
|
25
|
+
color_size: The number of colors the list should return. Optional.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
A generator of colors bounds in size by the color_size parameters or the gradient defaults color palette size
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def _hex2rgb(_color):
|
|
32
|
+
_color = _color.replace("#", "")
|
|
33
|
+
return tuple(int(_color[i : i + 2], 16) for i in (0, 2, 4))
|
|
34
|
+
|
|
35
|
+
if color_size < 0:
|
|
36
|
+
color_size = len(self.colors)
|
|
37
|
+
|
|
38
|
+
index = 0
|
|
39
|
+
if color:
|
|
40
|
+
color_rgb = _hex2rgb(color)
|
|
41
|
+
nearest_color = min(
|
|
42
|
+
self.colors, key=lambda subject: sum((s - q) ** 2 for s, q in zip(_hex2rgb(subject), color_rgb))
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
index = self.colors.index(nearest_color)
|
|
46
|
+
for i in range(color_size):
|
|
47
|
+
yield self.colors[(index + i) % len(self.colors)]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TransparencyMixin:
|
|
51
|
+
def get_cell_formatting(self, request):
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
"column": None,
|
|
55
|
+
"conditions": [
|
|
56
|
+
{
|
|
57
|
+
"condition": None,
|
|
58
|
+
"style": {"backgroundColor": f'rgba(255, 255, 255, {getattr(self, "TRANSPARENCY", 0.4)})'},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
}
|
|
62
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from django.apps import apps
|
|
2
|
+
from django.db.models.signals import pre_migrate
|
|
3
|
+
from pytest_factoryboy import register
|
|
4
|
+
from wbcore.contrib.color.factories import ColorGradientFactory
|
|
5
|
+
from wbcore.contrib.geography.tests.signals import app_pre_migration
|
|
6
|
+
|
|
7
|
+
register(ColorGradientFactory)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
pre_migrate.connect(app_pre_migration, sender=apps.get_app_config("geography"))
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from wbcore.contrib.color.factories import ColorGradientFactory
|
|
3
|
+
from wbcore.contrib.color.models import ColorGradient
|
|
4
|
+
|
|
5
|
+
COLORS = ["#FFFFFF", "#000000", "#FF5733", "#3357FF", "#FF33A1", "#800080"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestColorGradientModel:
|
|
9
|
+
@pytest.mark.django_db
|
|
10
|
+
def integration_test(self, mocker):
|
|
11
|
+
gradient = ColorGradientFactory()
|
|
12
|
+
assert ColorGradient.objects.filter(id=gradient.id).exists()
|
|
13
|
+
|
|
14
|
+
def test_str_method(self, mocker):
|
|
15
|
+
gradient = ColorGradient()
|
|
16
|
+
mocker.patch.object(gradient, "title", "My Gradient")
|
|
17
|
+
assert str(gradient) == "My Gradient"
|
|
18
|
+
|
|
19
|
+
def test_default_colors(self):
|
|
20
|
+
gradient = ColorGradient()
|
|
21
|
+
assert gradient.colors == []
|
|
22
|
+
|
|
23
|
+
def test_get_gradient(self, mocker):
|
|
24
|
+
gradient = ColorGradient()
|
|
25
|
+
assert list(gradient.get_gradient()) == []
|
|
26
|
+
|
|
27
|
+
def test_get_gradient_no_color_argument(self, mocker):
|
|
28
|
+
gradient = ColorGradient()
|
|
29
|
+
mocker.patch.object(gradient, "colors", COLORS)
|
|
30
|
+
palette = list(gradient.get_gradient(color=None, color_size=3))
|
|
31
|
+
assert len(palette) == 3
|
|
32
|
+
|
|
33
|
+
def test_get_gradient_with_non_existent_color(self, mocker):
|
|
34
|
+
gradient = ColorGradient()
|
|
35
|
+
mocker.patch.object(gradient, "colors", COLORS)
|
|
36
|
+
non_existent_color = "#123456"
|
|
37
|
+
palette = list(gradient.get_gradient(color=non_existent_color))
|
|
38
|
+
assert len(palette) == len(COLORS)
|
|
39
|
+
assert palette == ["#000000", "#FF5733", "#3357FF", "#FF33A1", "#800080", "#FFFFFF"]
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize("color_size", [20, 1, 0, -1])
|
|
42
|
+
def test_get_gradient_with_different_size(self, color_size, mocker):
|
|
43
|
+
gradient = ColorGradient()
|
|
44
|
+
mocker.patch.object(gradient, "colors", COLORS)
|
|
45
|
+
palette = list(gradient.get_gradient(color_size=color_size))
|
|
46
|
+
base_colors = gradient.colors
|
|
47
|
+
assert len(palette) == color_size if color_size > 0 else len(base_colors)
|
|
48
|
+
for index, color in enumerate(palette):
|
|
49
|
+
assert color == base_colors[index % len(base_colors)]
|
|
50
|
+
|
|
51
|
+
@pytest.mark.parametrize("color", COLORS)
|
|
52
|
+
def test_get_gradient_with_different_color(self, color, mocker):
|
|
53
|
+
gradient = ColorGradient()
|
|
54
|
+
mocker.patch.object(gradient, "colors", COLORS)
|
|
55
|
+
base_colors = gradient.colors
|
|
56
|
+
|
|
57
|
+
color_index = base_colors.index(color)
|
|
58
|
+
palette = list(gradient.get_gradient(color=color))
|
|
59
|
+
|
|
60
|
+
assert len(palette) == len(base_colors)
|
|
61
|
+
assert palette == base_colors[color_index:] + base_colors[:color_index]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from django.core.exceptions import ValidationError
|
|
3
|
+
from wbcore.contrib.color.fields import validate_color
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestColorield:
|
|
7
|
+
@pytest.mark.parametrize(
|
|
8
|
+
("input", "valid"),
|
|
9
|
+
[
|
|
10
|
+
("#1234567", False),
|
|
11
|
+
("259868", False),
|
|
12
|
+
("#25986", False),
|
|
13
|
+
("", False),
|
|
14
|
+
("#d225b0", True),
|
|
15
|
+
("#4839a3", True),
|
|
16
|
+
("#259868", True),
|
|
17
|
+
(None, True),
|
|
18
|
+
],
|
|
19
|
+
)
|
|
20
|
+
def test_color_validator(self, input, valid):
|
|
21
|
+
if valid:
|
|
22
|
+
assert input == validate_color(input)
|
|
23
|
+
else:
|
|
24
|
+
with pytest.raises(ValidationError):
|
|
25
|
+
validate_color(input)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from django.apps import apps
|
|
3
|
+
from django.db import connection
|
|
4
|
+
from django.db.models.signals import pre_migrate
|
|
5
|
+
from pytest_factoryboy import register
|
|
6
|
+
from wbcore.contrib.documents.factories import (
|
|
7
|
+
DocumentFactory,
|
|
8
|
+
DocumentTypeFactory,
|
|
9
|
+
ShareableLinkAccessFactory,
|
|
10
|
+
ShareableLinkFactory,
|
|
11
|
+
)
|
|
12
|
+
from wbcore.contrib.geography.tests.signals import app_pre_migration
|
|
13
|
+
from wbcore.tests.conftest import *
|
|
14
|
+
|
|
15
|
+
register(DocumentFactory)
|
|
16
|
+
register(DocumentTypeFactory)
|
|
17
|
+
register(ShareableLinkAccessFactory)
|
|
18
|
+
register(ShareableLinkFactory)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture(
|
|
22
|
+
autouse=True, scope="session"
|
|
23
|
+
) # Might want to find a way to registered default conftest logic automatically
|
|
24
|
+
def django_test_environment(django_test_environment):
|
|
25
|
+
from django.apps import apps
|
|
26
|
+
|
|
27
|
+
get_models = apps.get_models
|
|
28
|
+
|
|
29
|
+
for m in [m for m in get_models() if not m._meta.managed]:
|
|
30
|
+
m._meta.managed = True
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from django.core import mail
|
|
7
|
+
from django.core.validators import URLValidator
|
|
8
|
+
from django.utils import timezone
|
|
9
|
+
from faker import Faker
|
|
10
|
+
from slugify import slugify
|
|
11
|
+
from wbcore.contrib.documents.models.documents import (
|
|
12
|
+
Document,
|
|
13
|
+
send_email_as_task,
|
|
14
|
+
upload_to,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
fake = Faker()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.mark.django_db
|
|
21
|
+
class TestDocumentModel:
|
|
22
|
+
def test_init(self, document):
|
|
23
|
+
assert document.id is not None
|
|
24
|
+
|
|
25
|
+
@pytest.mark.parametrize(
|
|
26
|
+
"filename",
|
|
27
|
+
[(fake.file_name())],
|
|
28
|
+
)
|
|
29
|
+
def test_upload_to(self, document, document_type_factory, filename):
|
|
30
|
+
parent_document_type = document_type_factory.create()
|
|
31
|
+
document_type = document_type_factory.create(parent=parent_document_type)
|
|
32
|
+
document.document_type = document_type
|
|
33
|
+
document.save()
|
|
34
|
+
assert (
|
|
35
|
+
upload_to(document, filename)
|
|
36
|
+
== f"dms/document/{slugify(parent_document_type.name)}/{slugify(document_type.name)}/{filename}"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize(
|
|
40
|
+
"document__file",
|
|
41
|
+
[(fake.file_path())],
|
|
42
|
+
)
|
|
43
|
+
def test_filename(self, document):
|
|
44
|
+
assert document.filename == os.path.basename(document.file.name)
|
|
45
|
+
|
|
46
|
+
def test_linked_objects(self, document_factory):
|
|
47
|
+
document = document_factory.create()
|
|
48
|
+
link_obj = document_factory.create()
|
|
49
|
+
document.link(link_obj)
|
|
50
|
+
assert list(document.linked_objects)[0] == link_obj
|
|
51
|
+
|
|
52
|
+
@pytest.mark.parametrize(
|
|
53
|
+
"sharing_seconds_duration, one_time_link",
|
|
54
|
+
[(0, False), (fake.pyint(min_value=1), False), (0, True), (fake.pyint(min_value=1), True)],
|
|
55
|
+
)
|
|
56
|
+
def test_generate_shareable_link(self, document, sharing_seconds_duration, one_time_link):
|
|
57
|
+
document.generate_shareable_link(
|
|
58
|
+
sharing_seconds_duration=sharing_seconds_duration, one_time_link=one_time_link
|
|
59
|
+
)
|
|
60
|
+
assert (
|
|
61
|
+
document.shareable_links.filter(
|
|
62
|
+
valid_until__isnull=sharing_seconds_duration == 0, one_time_link=one_time_link
|
|
63
|
+
).count()
|
|
64
|
+
== 1
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@patch("wbcore.contrib.documents.models.documents.send_email_as_task.delay")
|
|
68
|
+
@pytest.mark.parametrize(
|
|
69
|
+
"to_emails",
|
|
70
|
+
[(fake.email())],
|
|
71
|
+
)
|
|
72
|
+
def test_send_email(self, mock_fct, document, to_emails):
|
|
73
|
+
document.send_email(to_emails)
|
|
74
|
+
assert mock_fct.call_count == 1
|
|
75
|
+
|
|
76
|
+
def test_get_for_object(self, document_factory):
|
|
77
|
+
document = document_factory.create()
|
|
78
|
+
link_obj = document_factory.create()
|
|
79
|
+
document.link(link_obj)
|
|
80
|
+
assert Document.get_for_object(link_obj).first() == document
|
|
81
|
+
|
|
82
|
+
@pytest.mark.parametrize(
|
|
83
|
+
"to_emails, as_link, subject, from_email, body",
|
|
84
|
+
[
|
|
85
|
+
(fake.email(), False, None, None, None),
|
|
86
|
+
(fake.email(), False, fake.paragraph(), fake.email(), fake.paragraph()),
|
|
87
|
+
(fake.email(), True, None, None, None),
|
|
88
|
+
],
|
|
89
|
+
)
|
|
90
|
+
def test_send_email_as_task(self, document, to_emails, as_link, subject, from_email, body):
|
|
91
|
+
assert len(mail.outbox) == 0
|
|
92
|
+
send_email_as_task(document.id, to_emails, as_link=as_link, subject=subject, from_email=from_email, body=body)
|
|
93
|
+
assert len(mail.outbox) == 1
|
|
94
|
+
msg = mail.outbox[0]
|
|
95
|
+
if as_link:
|
|
96
|
+
assert document.shareable_links.count() == 1
|
|
97
|
+
assert len(msg.attachments) == 0
|
|
98
|
+
assert len(msg.alternatives) == 1
|
|
99
|
+
else:
|
|
100
|
+
assert document.shareable_links.count() == 0
|
|
101
|
+
assert len(msg.attachments) == 1
|
|
102
|
+
assert len(msg.alternatives) == 2
|
|
103
|
+
assert msg
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@pytest.mark.django_db
|
|
107
|
+
class TestDocumentTypeModel:
|
|
108
|
+
def test_init(self, document_type):
|
|
109
|
+
assert document_type.id is not None
|
|
110
|
+
|
|
111
|
+
def test_str(self, document_type_factory):
|
|
112
|
+
document_type = document_type_factory.create(parent=document_type_factory.create())
|
|
113
|
+
document_type.save()
|
|
114
|
+
assert str(document_type) == document_type.compute_str()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.mark.django_db
|
|
118
|
+
class TestShareableLinkModel:
|
|
119
|
+
@pytest.mark.parametrize(
|
|
120
|
+
"shareable_link__manual_invalid, shareable_link__valid_until, shareable_link__one_time_link",
|
|
121
|
+
[(True, None, False), (False, timezone.now() - timedelta(seconds=60), False), (False, None, True)],
|
|
122
|
+
)
|
|
123
|
+
def test_unvalid_link(self, shareable_link):
|
|
124
|
+
if shareable_link.one_time_link:
|
|
125
|
+
assert shareable_link.is_valid()
|
|
126
|
+
shareable_link.access()
|
|
127
|
+
assert not shareable_link.is_valid()
|
|
128
|
+
|
|
129
|
+
def test_link(self, shareable_link):
|
|
130
|
+
URLValidator()(shareable_link.link)
|
|
131
|
+
|
|
132
|
+
def test_access(self, shareable_link):
|
|
133
|
+
assert shareable_link.accesses.count() == 0
|
|
134
|
+
shareable_link.access()
|
|
135
|
+
assert shareable_link.accesses.count() == 1
|
|
136
|
+
|
|
137
|
+
def test_init(self, shareable_link):
|
|
138
|
+
assert shareable_link.id is not None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.django_db
|
|
142
|
+
class TestShareableLinkAccessModel:
|
|
143
|
+
def test_init(self, shareable_link_access):
|
|
144
|
+
assert shareable_link_access.id is not None
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from django.db import IntegrityError
|
|
3
|
+
from wbcore.contrib.example_app.factories import (
|
|
4
|
+
EventFactory,
|
|
5
|
+
EventTypeFactory,
|
|
6
|
+
PlayerFactory,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.django_db
|
|
11
|
+
class TestEvent:
|
|
12
|
+
def test_str(self):
|
|
13
|
+
event = EventFactory(
|
|
14
|
+
minute=69,
|
|
15
|
+
person__first_name="Test",
|
|
16
|
+
person__last_name="Person",
|
|
17
|
+
event_type__name="Test Event Type",
|
|
18
|
+
match__sport__match_duration=90,
|
|
19
|
+
)
|
|
20
|
+
assert event.__str__() == "Test Event Type (69.) - Test Person"
|
|
21
|
+
|
|
22
|
+
@pytest.mark.parametrize(
|
|
23
|
+
"home, away, points, home_score, away_score", [(2, 1, 5, 7, 1), (0, 3, 2, 2, 3), (1, 1, 0, 1, 1)]
|
|
24
|
+
)
|
|
25
|
+
def test_save_home_scored(self, home, away, points, home_score, away_score):
|
|
26
|
+
person = PlayerFactory()
|
|
27
|
+
event = EventFactory(
|
|
28
|
+
person=person,
|
|
29
|
+
match__home=person.current_team,
|
|
30
|
+
match__score_home=home,
|
|
31
|
+
match__score_away=away,
|
|
32
|
+
event_type__points=points,
|
|
33
|
+
)
|
|
34
|
+
assert event.match.score_home == home_score
|
|
35
|
+
assert event.match.score_away == away_score
|
|
36
|
+
|
|
37
|
+
@pytest.mark.parametrize(
|
|
38
|
+
"home, away, points, home_score, away_score", [(41, 27, 4, 41, 31), (19, 0, 2, 19, 2), (22, 23, 0, 22, 23)]
|
|
39
|
+
)
|
|
40
|
+
def test_save_away_scored(self, home, away, points, home_score, away_score):
|
|
41
|
+
person = PlayerFactory()
|
|
42
|
+
event = EventFactory(
|
|
43
|
+
person=person,
|
|
44
|
+
match__away=person.current_team,
|
|
45
|
+
match__score_home=home,
|
|
46
|
+
match__score_away=away,
|
|
47
|
+
event_type__points=points,
|
|
48
|
+
)
|
|
49
|
+
assert event.match.score_home == home_score
|
|
50
|
+
assert event.match.score_away == away_score
|
|
51
|
+
|
|
52
|
+
def test_save_no_team(self):
|
|
53
|
+
person = PlayerFactory()
|
|
54
|
+
event = EventFactory(
|
|
55
|
+
person=person,
|
|
56
|
+
match__score_home=2,
|
|
57
|
+
match__score_away=1,
|
|
58
|
+
event_type__points=4,
|
|
59
|
+
)
|
|
60
|
+
assert event.match.score_home == 2
|
|
61
|
+
assert event.match.score_away == 1
|
|
62
|
+
|
|
63
|
+
def test_save_no_player(self):
|
|
64
|
+
event = EventFactory(
|
|
65
|
+
match__score_home=2,
|
|
66
|
+
match__score_away=1,
|
|
67
|
+
event_type__points=4,
|
|
68
|
+
)
|
|
69
|
+
assert event.match.score_home == 2
|
|
70
|
+
assert event.match.score_away == 1
|
|
71
|
+
|
|
72
|
+
def test_unique_constraint(self):
|
|
73
|
+
event = EventFactory()
|
|
74
|
+
with pytest.raises(IntegrityError):
|
|
75
|
+
EventFactory(person=event.person, minute=event.minute, event_type=event.event_type, match=event.match)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@pytest.mark.django_db
|
|
79
|
+
class TestEventType:
|
|
80
|
+
def test_str(self):
|
|
81
|
+
event_type = EventTypeFactory(name="Test Event Type")
|
|
82
|
+
assert event_type.__str__() == "Test Event Type"
|
|
83
|
+
|
|
84
|
+
def test_unique_constraint(self):
|
|
85
|
+
event_type = EventTypeFactory()
|
|
86
|
+
with pytest.raises(IntegrityError):
|
|
87
|
+
EventTypeFactory(name=event_type.name, sport=event_type.sport)
|