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,128 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from wbcore.contrib.workflow.factories import UserStepFactory
|
|
5
|
+
from wbcore.contrib.workflow.models import Condition
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.django_db
|
|
9
|
+
class TestTransition:
|
|
10
|
+
def test_all_conditions_satisfied_no_conditions(self, transition_factory, process_step_factory):
|
|
11
|
+
transition = transition_factory()
|
|
12
|
+
process_step = process_step_factory()
|
|
13
|
+
assert transition.all_conditions_satisfied(process_step)
|
|
14
|
+
|
|
15
|
+
@patch("wbcore.contrib.workflow.models.step.Step.set_failed")
|
|
16
|
+
@patch("wbcore.contrib.workflow.models.process.ProcessStep.get_instance")
|
|
17
|
+
def test_all_conditions_satisfied_no_instance(
|
|
18
|
+
self,
|
|
19
|
+
mock_get_instance,
|
|
20
|
+
mock_failed,
|
|
21
|
+
random_child_step_factory,
|
|
22
|
+
transition_factory,
|
|
23
|
+
condition_factory,
|
|
24
|
+
process_step_factory,
|
|
25
|
+
):
|
|
26
|
+
transition = transition_factory()
|
|
27
|
+
condition_factory(transition=transition)
|
|
28
|
+
step = random_child_step_factory(exclude_factories=[UserStepFactory])
|
|
29
|
+
process_step = process_step_factory(step=step)
|
|
30
|
+
mock_get_instance.return_value = None
|
|
31
|
+
assert not transition.all_conditions_satisfied(process_step)
|
|
32
|
+
assert mock_failed.call_args.args[0] == process_step
|
|
33
|
+
|
|
34
|
+
@patch("wbcore.contrib.workflow.models.process.ProcessStep.get_instance")
|
|
35
|
+
@patch("wbcore.contrib.workflow.models.condition.Condition.satisfied")
|
|
36
|
+
def test_all_conditions_satisfied(
|
|
37
|
+
self, mock_satisfied, mock_get_instance, transition_factory, condition_factory, process_step_factory
|
|
38
|
+
):
|
|
39
|
+
transition = transition_factory()
|
|
40
|
+
process_step = process_step_factory()
|
|
41
|
+
attached_instance = process_step.process.instance
|
|
42
|
+
condition_factory()
|
|
43
|
+
condition_factory(transition=transition)
|
|
44
|
+
condition_factory(transition=transition)
|
|
45
|
+
mock_get_instance.return_value = attached_instance
|
|
46
|
+
mock_satisfied.return_value = True
|
|
47
|
+
assert transition.all_conditions_satisfied(process_step)
|
|
48
|
+
assert mock_satisfied.call_args.args == (attached_instance,)
|
|
49
|
+
assert mock_satisfied.call_count == 2
|
|
50
|
+
|
|
51
|
+
@patch("wbcore.contrib.workflow.models.step.Step.set_failed")
|
|
52
|
+
@patch("wbcore.contrib.workflow.models.process.ProcessStep.get_instance")
|
|
53
|
+
def test_all_conditions_satisfied_errors(
|
|
54
|
+
self,
|
|
55
|
+
mock_get_instance,
|
|
56
|
+
mock_failed,
|
|
57
|
+
random_child_step_factory,
|
|
58
|
+
transition_factory,
|
|
59
|
+
condition_factory,
|
|
60
|
+
process_step_factory,
|
|
61
|
+
):
|
|
62
|
+
transition = transition_factory()
|
|
63
|
+
step = random_child_step_factory(exclude_factories=[UserStepFactory])
|
|
64
|
+
process_step = process_step_factory(step=step)
|
|
65
|
+
attached_instance = process_step.process.instance
|
|
66
|
+
condition_factory()
|
|
67
|
+
condition_factory(
|
|
68
|
+
transition=transition,
|
|
69
|
+
operator=Condition.Operator.EQ,
|
|
70
|
+
expected_value=attached_instance.first_name,
|
|
71
|
+
negate_operator=False,
|
|
72
|
+
)
|
|
73
|
+
condition_factory(
|
|
74
|
+
transition=transition,
|
|
75
|
+
attribute_name="last_name",
|
|
76
|
+
operator=Condition.Operator.EQ,
|
|
77
|
+
expected_value=attached_instance.last_name,
|
|
78
|
+
negate_operator=False,
|
|
79
|
+
)
|
|
80
|
+
condition_factory(
|
|
81
|
+
transition=transition,
|
|
82
|
+
attribute_name="Test",
|
|
83
|
+
operator=Condition.Operator.EQ,
|
|
84
|
+
expected_value=attached_instance.first_name,
|
|
85
|
+
negate_operator=True,
|
|
86
|
+
)
|
|
87
|
+
mock_get_instance.return_value = attached_instance
|
|
88
|
+
assert not transition.all_conditions_satisfied(process_step)
|
|
89
|
+
assert mock_failed.call_args.args[0] == process_step
|
|
90
|
+
|
|
91
|
+
@patch("wbcore.contrib.workflow.models.step.Step.set_failed")
|
|
92
|
+
@patch("wbcore.contrib.workflow.models.process.ProcessStep.get_instance")
|
|
93
|
+
def test_all_conditions_satisfied_false(
|
|
94
|
+
self,
|
|
95
|
+
mock_get_instance,
|
|
96
|
+
mock_failed,
|
|
97
|
+
random_child_step_factory,
|
|
98
|
+
transition_factory,
|
|
99
|
+
condition_factory,
|
|
100
|
+
process_step_factory,
|
|
101
|
+
):
|
|
102
|
+
transition = transition_factory()
|
|
103
|
+
step = random_child_step_factory(exclude_factories=[UserStepFactory])
|
|
104
|
+
process_step = process_step_factory(step=step)
|
|
105
|
+
attached_instance = process_step.process.instance
|
|
106
|
+
condition_factory()
|
|
107
|
+
condition_factory(
|
|
108
|
+
transition=transition,
|
|
109
|
+
operator=Condition.Operator.EQ,
|
|
110
|
+
expected_value=attached_instance.first_name,
|
|
111
|
+
negate_operator=False,
|
|
112
|
+
)
|
|
113
|
+
condition_factory(
|
|
114
|
+
transition=transition,
|
|
115
|
+
operator=Condition.Operator.EQ,
|
|
116
|
+
expected_value=attached_instance.first_name,
|
|
117
|
+
negate_operator=True,
|
|
118
|
+
)
|
|
119
|
+
condition_factory(
|
|
120
|
+
transition=transition,
|
|
121
|
+
attribute_name="last_name",
|
|
122
|
+
operator=Condition.Operator.EQ,
|
|
123
|
+
expected_value=attached_instance.last_name,
|
|
124
|
+
negate_operator=False,
|
|
125
|
+
)
|
|
126
|
+
mock_get_instance.return_value = attached_instance
|
|
127
|
+
assert not transition.all_conditions_satisfied(process_step)
|
|
128
|
+
assert not mock_failed.called
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import factory
|
|
4
|
+
import pytest
|
|
5
|
+
from django.contrib.contenttypes.models import ContentType
|
|
6
|
+
from wbcore.contrib.authentication.factories import GroupFactory, UserFactory
|
|
7
|
+
from wbcore.contrib.authentication.models import Permission
|
|
8
|
+
from wbcore.contrib.directory.factories import CompanyFactory, PersonFactory
|
|
9
|
+
from wbcore.contrib.directory.models import Person
|
|
10
|
+
from wbcore.contrib.workflow.models import (
|
|
11
|
+
Condition,
|
|
12
|
+
DataValue,
|
|
13
|
+
Process,
|
|
14
|
+
ProcessStep,
|
|
15
|
+
Transition,
|
|
16
|
+
Workflow,
|
|
17
|
+
)
|
|
18
|
+
from wbcore.contrib.workflow.models.workflow import re_render_workflow_graph
|
|
19
|
+
from wbcore.contrib.workflow.sites import workflow_site
|
|
20
|
+
from wbcore.test.utils import get_or_create_superuser
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.django_db
|
|
24
|
+
class TestWorkflow:
|
|
25
|
+
@patch("wbcore.contrib.workflow.models.workflow.transaction.on_commit")
|
|
26
|
+
def test_start_workflow_without_attached_instance(
|
|
27
|
+
self, mock_commit, workflow_factory, data_factory, start_step_factory
|
|
28
|
+
):
|
|
29
|
+
workflow = workflow_factory(model=None, status_field=None, preserve_instance=False)
|
|
30
|
+
data1 = data_factory(workflow=workflow)
|
|
31
|
+
default_value = "Test"
|
|
32
|
+
data2 = data_factory(workflow=workflow, default=default_value)
|
|
33
|
+
start_step = start_step_factory(workflow=workflow)
|
|
34
|
+
workflow.start_workflow(start_step)
|
|
35
|
+
process_qs = Process.objects.filter(workflow=workflow, instance_id=None, content_type=None)
|
|
36
|
+
assert process_qs.exists()
|
|
37
|
+
assert DataValue.objects.filter(data=data1, process=process_qs.first()).exists()
|
|
38
|
+
assert DataValue.objects.filter(data=data2, process=process_qs.first(), value=default_value).exists()
|
|
39
|
+
assert mock_commit.called
|
|
40
|
+
|
|
41
|
+
@patch("wbcore.contrib.workflow.models.workflow.transaction.on_commit")
|
|
42
|
+
def test_start_workflow_with_attached_instance(
|
|
43
|
+
self, mock_commit, workflow_factory, start_step_factory, data_factory
|
|
44
|
+
):
|
|
45
|
+
attached_person = PersonFactory()
|
|
46
|
+
workflow = workflow_factory(preserve_instance=True)
|
|
47
|
+
data1 = data_factory(workflow=workflow)
|
|
48
|
+
default_value = "Test"
|
|
49
|
+
data2 = data_factory(workflow=workflow, default=default_value)
|
|
50
|
+
start_step = start_step_factory()
|
|
51
|
+
workflow_site.registered_model_classes_serializer_map[
|
|
52
|
+
Person
|
|
53
|
+
] = "wbcore.contrib.directory.serializers.PersonModelSerializer"
|
|
54
|
+
workflow.start_workflow(start_step, attached_person)
|
|
55
|
+
process_qs = Process.objects.filter(
|
|
56
|
+
workflow=workflow,
|
|
57
|
+
instance_id=attached_person.pk,
|
|
58
|
+
content_type=ContentType.objects.get_for_model(Person),
|
|
59
|
+
preserved_instance__isnull=False,
|
|
60
|
+
)
|
|
61
|
+
assert process_qs.exists()
|
|
62
|
+
assert DataValue.objects.filter(data=data1, process=process_qs.first()).exists()
|
|
63
|
+
assert DataValue.objects.filter(data=data2, process=process_qs.first(), value=default_value).exists()
|
|
64
|
+
assert mock_commit.called
|
|
65
|
+
|
|
66
|
+
def test_get_start_steps_for_workflow(self, workflow_factory, start_step_factory):
|
|
67
|
+
workflow = workflow_factory(model=None, single_instance_execution=False, status_field=None)
|
|
68
|
+
start_step = start_step_factory(workflow=workflow)
|
|
69
|
+
start_step_factory()
|
|
70
|
+
start_transitions = workflow.get_start_steps_for_workflow()
|
|
71
|
+
assert list(start_transitions) == [start_step]
|
|
72
|
+
|
|
73
|
+
def test_get_start_steps_for_workflow_single_instance_execution(
|
|
74
|
+
self, workflow_factory, process_factory, start_step_factory
|
|
75
|
+
):
|
|
76
|
+
workflow = workflow_factory(model=None, single_instance_execution=True, status_field=None)
|
|
77
|
+
start_step_factory(workflow=workflow)
|
|
78
|
+
start_step_factory()
|
|
79
|
+
process_factory(state=Process.ProcessState.ACTIVE, workflow=workflow)
|
|
80
|
+
assert not workflow.get_start_steps_for_workflow().exists()
|
|
81
|
+
|
|
82
|
+
def test_get_start_steps_for_workflow_single_instance_execution_no_running_process(
|
|
83
|
+
self, workflow_factory, process_factory, start_step_factory
|
|
84
|
+
):
|
|
85
|
+
workflow = workflow_factory(model=None, single_instance_execution=True, status_field=None)
|
|
86
|
+
start_step = start_step_factory(workflow=workflow)
|
|
87
|
+
start_step_factory()
|
|
88
|
+
process_factory(state=Process.ProcessState.FINISHED, workflow=workflow)
|
|
89
|
+
start_transitions = workflow.get_start_steps_for_workflow()
|
|
90
|
+
assert list(start_transitions) == [start_step]
|
|
91
|
+
|
|
92
|
+
def test_get_start_steps_for_workflow_with_attached_instance(self, workflow_factory, start_step_factory):
|
|
93
|
+
workflow = workflow_factory()
|
|
94
|
+
start_step_factory(workflow=workflow)
|
|
95
|
+
start_step_factory()
|
|
96
|
+
assert not workflow.get_start_steps_for_workflow().exists()
|
|
97
|
+
|
|
98
|
+
def test_get_start_steps_for_instance(self, workflow_factory, start_step_factory):
|
|
99
|
+
person = PersonFactory()
|
|
100
|
+
workflow = workflow_factory()
|
|
101
|
+
start_step = start_step_factory(workflow=workflow, status=person.first_name)
|
|
102
|
+
start_step_factory(workflow=workflow)
|
|
103
|
+
assert Workflow.get_start_steps_for_instance(person) == [start_step]
|
|
104
|
+
|
|
105
|
+
def test_get_start_steps_for_instance_no_model_attached(self, workflow_factory, start_step_factory):
|
|
106
|
+
person = PersonFactory()
|
|
107
|
+
workflow = workflow_factory(model=None, status_field=None)
|
|
108
|
+
start_step_factory(workflow=workflow, status=person.first_name)
|
|
109
|
+
assert not Workflow.get_start_steps_for_instance(person)
|
|
110
|
+
|
|
111
|
+
def test_get_start_steps_for_instance_wrong_instance(self, workflow_factory, start_step_factory):
|
|
112
|
+
company = CompanyFactory()
|
|
113
|
+
workflow = workflow_factory()
|
|
114
|
+
start_step_factory(workflow=workflow)
|
|
115
|
+
assert not Workflow.get_start_steps_for_instance(company)
|
|
116
|
+
|
|
117
|
+
def test_get_start_steps_for_instance_single_instance_execution(
|
|
118
|
+
self, workflow_factory, process_factory, start_step_factory
|
|
119
|
+
):
|
|
120
|
+
person = PersonFactory()
|
|
121
|
+
workflow = workflow_factory(single_instance_execution=True)
|
|
122
|
+
start_step_factory(workflow=workflow, status=person.first_name)
|
|
123
|
+
process_factory(finished=None, workflow=workflow, instance=person)
|
|
124
|
+
assert not Workflow.get_start_steps_for_instance(person)
|
|
125
|
+
|
|
126
|
+
def test_get_start_steps_for_instance_single_instance_execution_no_running_process(
|
|
127
|
+
self, workflow_factory, process_factory, start_step_factory
|
|
128
|
+
):
|
|
129
|
+
attached_instance = PersonFactory()
|
|
130
|
+
workflow = workflow_factory(single_instance_execution=True)
|
|
131
|
+
start_step = start_step_factory(workflow=workflow, status=attached_instance.first_name)
|
|
132
|
+
start_step_factory()
|
|
133
|
+
process_factory(workflow=workflow, instance=attached_instance)
|
|
134
|
+
wrong_instance = CompanyFactory()
|
|
135
|
+
process_factory(workflow=workflow, finished=None, instance=wrong_instance)
|
|
136
|
+
assert Workflow.get_start_steps_for_instance(attached_instance) == [start_step]
|
|
137
|
+
|
|
138
|
+
def test_get_start_steps_for_instance_wrong_status_field(self, workflow_factory, start_step_factory):
|
|
139
|
+
person = PersonFactory()
|
|
140
|
+
workflow = workflow_factory(status_field="Test")
|
|
141
|
+
start_step_factory(workflow=workflow, status=person.first_name)
|
|
142
|
+
assert not Workflow.get_start_steps_for_instance(person)
|
|
143
|
+
|
|
144
|
+
def test_get_start_steps_for_instance_wrong_status(self, workflow_factory, start_step_factory):
|
|
145
|
+
person = PersonFactory()
|
|
146
|
+
workflow = workflow_factory()
|
|
147
|
+
start_step_factory(workflow=workflow, status="Test")
|
|
148
|
+
assert not Workflow.get_start_steps_for_instance(person)
|
|
149
|
+
|
|
150
|
+
def test_get_next_user_step_transitions_for_instance_superuser(
|
|
151
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
152
|
+
):
|
|
153
|
+
workflow = workflow_factory()
|
|
154
|
+
step = user_step_factory(workflow=workflow, permission=Permission.objects.last())
|
|
155
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
156
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE)
|
|
157
|
+
attached_instance = process_step.process.instance
|
|
158
|
+
superuser = get_or_create_superuser()
|
|
159
|
+
start_transitions = Workflow.get_next_user_step_transitions_for_instance(attached_instance, superuser)
|
|
160
|
+
assert start_transitions[0][0] == [transition]
|
|
161
|
+
assert str(start_transitions[0][1].pk) == process_step.pk
|
|
162
|
+
|
|
163
|
+
def test_get_next_user_step_transitions_for_instance_wrong_instance(
|
|
164
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
165
|
+
):
|
|
166
|
+
workflow = workflow_factory()
|
|
167
|
+
instance = PersonFactory()
|
|
168
|
+
step = user_step_factory(workflow=workflow, permission=Permission.objects.last())
|
|
169
|
+
transition_factory(from_step=step, to_step__workflow=workflow)
|
|
170
|
+
process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE)
|
|
171
|
+
superuser = get_or_create_superuser()
|
|
172
|
+
assert not Workflow.get_next_user_step_transitions_for_instance(instance, superuser)
|
|
173
|
+
|
|
174
|
+
def test_get_next_user_step_transitions_for_instance_user_is_assignee(
|
|
175
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
176
|
+
):
|
|
177
|
+
workflow = workflow_factory()
|
|
178
|
+
step = user_step_factory(workflow=workflow)
|
|
179
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
180
|
+
user = UserFactory()
|
|
181
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE, assignee=user)
|
|
182
|
+
attached_instance = process_step.process.instance
|
|
183
|
+
start_transitions = Workflow.get_next_user_step_transitions_for_instance(attached_instance, user)
|
|
184
|
+
assert start_transitions[0][0] == [transition]
|
|
185
|
+
assert str(start_transitions[0][1].pk) == process_step.pk
|
|
186
|
+
|
|
187
|
+
def test_get_next_user_step_transitions_for_instance_user_in_assignee_group(
|
|
188
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
189
|
+
):
|
|
190
|
+
workflow = workflow_factory()
|
|
191
|
+
step = user_step_factory(workflow=workflow, assignee_method=None)
|
|
192
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
193
|
+
group = GroupFactory()
|
|
194
|
+
user = UserFactory(groups=[group])
|
|
195
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE, group=group)
|
|
196
|
+
attached_instance = process_step.process.instance
|
|
197
|
+
start_transitions = Workflow.get_next_user_step_transitions_for_instance(attached_instance, user)
|
|
198
|
+
assert start_transitions[0][0] == [transition]
|
|
199
|
+
assert str(start_transitions[0][1].pk) == process_step.pk
|
|
200
|
+
|
|
201
|
+
def test_get_next_user_step_transitions_for_instance_user_in_assignee_group_but_with_method(
|
|
202
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
203
|
+
):
|
|
204
|
+
workflow = workflow_factory()
|
|
205
|
+
step = user_step_factory(workflow=workflow, permission=Permission.objects.last())
|
|
206
|
+
transition_factory(from_step=step, to_step__workflow=workflow)
|
|
207
|
+
group = GroupFactory()
|
|
208
|
+
user = UserFactory(groups=[group])
|
|
209
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE, group=group)
|
|
210
|
+
attached_instance = process_step.process.instance
|
|
211
|
+
assert not Workflow.get_next_user_step_transitions_for_instance(attached_instance, user)
|
|
212
|
+
|
|
213
|
+
def test_get_next_user_step_transitions_for_instance_user_has_permission(
|
|
214
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
215
|
+
):
|
|
216
|
+
workflow = workflow_factory()
|
|
217
|
+
step = user_step_factory(workflow=workflow, permission=Permission.objects.last())
|
|
218
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
219
|
+
user = UserFactory()
|
|
220
|
+
user.user_permissions.add(step.permission)
|
|
221
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE, assignee=user)
|
|
222
|
+
attached_instance = process_step.process.instance
|
|
223
|
+
start_transitions = Workflow.get_next_user_step_transitions_for_instance(attached_instance, user)
|
|
224
|
+
assert start_transitions[0][0] == [transition]
|
|
225
|
+
assert str(start_transitions[0][1].pk) == process_step.pk
|
|
226
|
+
|
|
227
|
+
def test_get_next_user_step_transitions_for_instance_user_has_group_permission(
|
|
228
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
229
|
+
):
|
|
230
|
+
workflow = workflow_factory()
|
|
231
|
+
step = user_step_factory(workflow=workflow, permission=Permission.objects.last())
|
|
232
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
233
|
+
group = GroupFactory(permissions=[step.permission])
|
|
234
|
+
user = UserFactory(groups=[group])
|
|
235
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE, assignee=user)
|
|
236
|
+
attached_instance = process_step.process.instance
|
|
237
|
+
start_transitions = Workflow.get_next_user_step_transitions_for_instance(attached_instance, user)
|
|
238
|
+
assert start_transitions[0][0] == [transition]
|
|
239
|
+
assert str(start_transitions[0][1].pk) == process_step.pk
|
|
240
|
+
|
|
241
|
+
def test_get_next_user_step_transitions_for_instance_user_has_no_permission(
|
|
242
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
243
|
+
):
|
|
244
|
+
workflow = workflow_factory()
|
|
245
|
+
step = user_step_factory(workflow=workflow, permission=Permission.objects.last())
|
|
246
|
+
transition_factory(from_step=step, to_step__workflow=workflow)
|
|
247
|
+
user = UserFactory()
|
|
248
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE, assignee=user)
|
|
249
|
+
attached_instance = process_step.process.instance
|
|
250
|
+
assert not Workflow.get_next_user_step_transitions_for_instance(attached_instance, user)
|
|
251
|
+
|
|
252
|
+
def test_get_next_user_step_transitions_for_instance_user_no_permission_selected(
|
|
253
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory
|
|
254
|
+
):
|
|
255
|
+
workflow = workflow_factory()
|
|
256
|
+
step = user_step_factory(workflow=workflow)
|
|
257
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
258
|
+
user = UserFactory()
|
|
259
|
+
process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE, assignee=user)
|
|
260
|
+
attached_instance = process_step.process.instance
|
|
261
|
+
start_transitions = Workflow.get_next_user_step_transitions_for_instance(attached_instance, user)
|
|
262
|
+
assert start_transitions[0][0] == [transition]
|
|
263
|
+
assert str(start_transitions[0][1].pk) == process_step.pk
|
|
264
|
+
|
|
265
|
+
def test_get_next_user_step_transitions_for_instance_condition_satisfied(
|
|
266
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory, condition_factory
|
|
267
|
+
):
|
|
268
|
+
workflow = workflow_factory()
|
|
269
|
+
person = PersonFactory()
|
|
270
|
+
step = user_step_factory(workflow=workflow)
|
|
271
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
272
|
+
condition_factory(
|
|
273
|
+
transition=transition,
|
|
274
|
+
attribute_name="first_name",
|
|
275
|
+
expected_value=person.first_name,
|
|
276
|
+
operator=Condition.Operator.EQ,
|
|
277
|
+
negate_operator=False,
|
|
278
|
+
)
|
|
279
|
+
superuser = get_or_create_superuser()
|
|
280
|
+
process_step = process_step_factory(
|
|
281
|
+
step=step, state=ProcessStep.StepState.ACTIVE, assignee=superuser, process__instance=person
|
|
282
|
+
)
|
|
283
|
+
start_transitions = Workflow.get_next_user_step_transitions_for_instance(person, superuser)
|
|
284
|
+
assert start_transitions[0][0] == [transition]
|
|
285
|
+
assert str(start_transitions[0][1].pk) == process_step.pk
|
|
286
|
+
|
|
287
|
+
def test_get_next_user_step_transitions_for_instance_condition_not_satisfied(
|
|
288
|
+
self, workflow_factory, transition_factory, process_step_factory, user_step_factory, condition_factory
|
|
289
|
+
):
|
|
290
|
+
workflow = workflow_factory()
|
|
291
|
+
person = PersonFactory()
|
|
292
|
+
step = user_step_factory(workflow=workflow)
|
|
293
|
+
transition = transition_factory(from_step=step, to_step__workflow=workflow)
|
|
294
|
+
condition_factory(
|
|
295
|
+
transition=transition,
|
|
296
|
+
attribute_name="first_name",
|
|
297
|
+
expected_value=person.first_name,
|
|
298
|
+
operator=Condition.Operator.EQ,
|
|
299
|
+
negate_operator=True,
|
|
300
|
+
)
|
|
301
|
+
superuser = get_or_create_superuser()
|
|
302
|
+
process_step_factory(
|
|
303
|
+
step=step, state=ProcessStep.StepState.ACTIVE, assignee=superuser, process__instance=person
|
|
304
|
+
)
|
|
305
|
+
assert not Workflow.get_next_user_step_transitions_for_instance(person, superuser)
|
|
306
|
+
|
|
307
|
+
@patch("wbcore.contrib.workflow.models.workflow.Workflow.generate_workflow_png")
|
|
308
|
+
def test_re_render_workflow_graph_workflow(self, mock_generate, workflow_factory):
|
|
309
|
+
workflow = workflow_factory()
|
|
310
|
+
old_graph = workflow.graph
|
|
311
|
+
new_graph = factory.django.ImageField()._make_data({"width": 1024, "height": 768})
|
|
312
|
+
mock_generate.return_value = new_graph
|
|
313
|
+
re_render_workflow_graph(sender=Workflow, instance=workflow, raw=False)
|
|
314
|
+
assert mock_generate.call_count == 1
|
|
315
|
+
assert workflow.graph != old_graph
|
|
316
|
+
|
|
317
|
+
@patch("wbcore.contrib.workflow.models.workflow.Workflow.generate_workflow_png")
|
|
318
|
+
def test_re_render_workflow_graph_step(self, mock_generate, random_child_step_factory):
|
|
319
|
+
step = random_child_step_factory()
|
|
320
|
+
old_graph = step.workflow.graph
|
|
321
|
+
new_graph = factory.django.ImageField()._make_data({"width": 1024, "height": 768})
|
|
322
|
+
mock_generate.return_value = new_graph
|
|
323
|
+
re_render_workflow_graph(sender=step.__class__, instance=step, raw=False)
|
|
324
|
+
# Not really sure why its called twice but as long as we have no infinite loop I'm fine
|
|
325
|
+
assert mock_generate.call_count == 2
|
|
326
|
+
assert step.workflow.graph != old_graph
|
|
327
|
+
|
|
328
|
+
@patch("wbcore.contrib.workflow.models.workflow.Workflow.generate_workflow_png")
|
|
329
|
+
def test_re_render_workflow_graph_transition(self, mock_generate, transition_factory):
|
|
330
|
+
transition = transition_factory()
|
|
331
|
+
old_graph = transition.to_step.workflow.graph
|
|
332
|
+
new_graph = factory.django.ImageField()._make_data({"width": 1024, "height": 768})
|
|
333
|
+
mock_generate.return_value = new_graph
|
|
334
|
+
re_render_workflow_graph(sender=Transition, instance=transition, raw=False)
|
|
335
|
+
# Not really sure why its called twice but as long as we have no infinite loop I'm fine
|
|
336
|
+
assert mock_generate.call_count == 2
|
|
337
|
+
assert transition.to_step.workflow.graph != old_graph
|
|
338
|
+
|
|
339
|
+
@patch("wbcore.contrib.workflow.models.workflow.Workflow.generate_workflow_png")
|
|
340
|
+
def test_re_render_workflow_graph_condition(self, mock_generate, condition_factory):
|
|
341
|
+
condition = condition_factory()
|
|
342
|
+
old_graph = condition.transition.to_step.workflow.graph
|
|
343
|
+
new_graph = factory.django.ImageField()._make_data({"width": 1024, "height": 768})
|
|
344
|
+
mock_generate.return_value = new_graph
|
|
345
|
+
re_render_workflow_graph(sender=Condition, instance=condition, raw=False)
|
|
346
|
+
# Not really sure why its called twice but as long as we have no infinite loop I'm fine
|
|
347
|
+
assert mock_generate.call_count == 2
|
|
348
|
+
assert condition.transition.to_step.workflow.graph != old_graph
|
|
349
|
+
|
|
350
|
+
@patch("wbcore.contrib.workflow.models.workflow.Workflow.generate_workflow_png")
|
|
351
|
+
def test_re_render_workflow_graph_sender_renamed(self, mock_generate, workflow_factory):
|
|
352
|
+
workflow = workflow_factory()
|
|
353
|
+
old_graph = workflow.graph
|
|
354
|
+
new_graph = factory.django.ImageField()._make_data({"width": 1024, "height": 768})
|
|
355
|
+
mock_generate.return_value = new_graph
|
|
356
|
+
re_render_workflow_graph(sender=ProcessStep, instance=workflow, raw=False)
|
|
357
|
+
assert mock_generate.call_count == 0
|
|
358
|
+
assert workflow.graph == old_graph
|
|
File without changes
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from selenium.common.exceptions import TimeoutException
|
|
2
|
+
from selenium.webdriver.common.by import By
|
|
3
|
+
from selenium.webdriver.remote.webdriver import WebDriver
|
|
4
|
+
from selenium.webdriver.remote.webelement import WebElement
|
|
5
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
6
|
+
from selenium.webdriver.support.color import Color
|
|
7
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
|
8
|
+
|
|
9
|
+
from .e2e_helper_methods import find_element
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def does_background_color_match(element: WebElement, expected_color: str) -> bool:
|
|
13
|
+
"""Check if the background color of an element matches the expected color.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
element (WebElement): The element to check the background of.
|
|
17
|
+
expected_color (str): The expected color.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
bool: True if the background color is as expected
|
|
21
|
+
"""
|
|
22
|
+
element_rgb = element.value_of_css_property("background-color")
|
|
23
|
+
element_rgb = Color.from_string(element_rgb).rgb
|
|
24
|
+
return element_rgb == expected_color
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_text_visible(driver: WebDriver, text: str) -> bool:
|
|
28
|
+
"""Check if the searched text is visible on the current page.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
driver (WebDriver): The Selenium webdriver.
|
|
32
|
+
text (str): Searched text.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
bool: True if the text is visible
|
|
36
|
+
"""
|
|
37
|
+
return True if find_element(driver, f"//*[text()='{text}']") else False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_string_not_visible(driver: WebDriver, string: str) -> bool:
|
|
41
|
+
"""Check if the searched text is not visible on the current page.
|
|
42
|
+
Sometimes selenium is faster, that the page can refresh. So this method can wait up to 10 seconds for a string to not be visible.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
driver (WebDriver): The Selenium webdriver.
|
|
46
|
+
text (str): Searched text.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
bool: True if the text is not visible
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
WebDriverWait(driver, 5).until(EC.invisibility_of_element_located((By.XPATH, f"//*[text()='{string}']")))
|
|
53
|
+
return True
|
|
54
|
+
except TimeoutException:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def is_tag_visible(driver: WebDriver, tag_label: str) -> bool:
|
|
59
|
+
"""Check if a tag with a certain label is visible on the current page.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
driver (WebDriver): The Selenium webdriver.
|
|
63
|
+
text (str): The label text.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
bool: True if the label is visible
|
|
68
|
+
"""
|
|
69
|
+
return True if find_element(driver, f"//*[@class='tag-label' and text()='{tag_label}']") else False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def is_tag_not_visible(driver: WebDriver, tag_label: str) -> bool:
|
|
73
|
+
"""Check if a tag with a certain label is not visible on the current page.
|
|
74
|
+
Sometimes selenium is faster, that the page can refresh. So this method can wait up to 10 seconds for a tag to not be visible.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
driver (WebDriver): The Selenium webdriver.
|
|
78
|
+
text (str): The label text.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
bool: True if the tag is not visible
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
WebDriverWait(driver, 2.5).until(
|
|
85
|
+
EC.invisibility_of_element_located((By.XPATH, f"//*[@class='tag-label' and text()='{tag_label}']"))
|
|
86
|
+
)
|
|
87
|
+
return True
|
|
88
|
+
except TimeoutException:
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def is_error_visible(driver: WebDriver):
|
|
93
|
+
"""Check if an error element is visible on the current page.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
driver (WebDriver): The Selenium webdriver.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
bool: True if an error element is visible
|
|
100
|
+
"""
|
|
101
|
+
error_element = find_element(driver, "//div[contains(@type, 'error')]", 2.5)
|
|
102
|
+
saving_failed_hint = find_element(driver, "//div[contains(@class, 'task-dropper-content')]", 2.5)
|
|
103
|
+
|
|
104
|
+
if saving_failed_hint and saving_failed_hint.is_displayed():
|
|
105
|
+
WebDriverWait(driver, 10).until_not(
|
|
106
|
+
EC.invisibility_of_element_located((By.XPATH, "//div[contains(@class, 'task-dropper-content')]"))
|
|
107
|
+
)
|
|
108
|
+
return error_element is not None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def is_counter_as_expected(driver: WebDriver, count: int):
|
|
112
|
+
"""Check if list counter displays the right number.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
driver (WebDriver): The Selenium webdriver.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
bool: True if the counter is right.
|
|
119
|
+
"""
|
|
120
|
+
find_element(driver, f"//span[@class='ag-status-name-value-value' and text()='{count}']")
|
|
121
|
+
return find_element is not None
|