Skip to content

Commit 5fb7e55

Browse files
committed
add select plugin test
1 parent d628eea commit 5fb7e55

File tree

1 file changed

+336
-0
lines changed

1 file changed

+336
-0
lines changed

tests/test_select_plugin.py

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
from cms.api import add_plugin
2+
from cms.plugin_pool import plugin_pool
3+
from cms.test_utils.testcases import CMSTestCase
4+
from django.contrib.admin.sites import AdminSite
5+
from django.http import QueryDict
6+
7+
from djangocms_form_builder.cms_plugins.form_plugins import SelectPlugin
8+
from djangocms_form_builder.models import Select
9+
10+
from .fixtures import TestFixture
11+
12+
13+
class SelectPluginTests(TestFixture, CMSTestCase):
14+
def setUp(self):
15+
super().setUp()
16+
self.plugin_class = plugin_pool.get_plugin("SelectPlugin")
17+
self.admin_site = AdminSite()
18+
19+
def _create_select_plugin(self, **config_kwargs):
20+
"""Helper to create a SelectPlugin instance"""
21+
defaults = {
22+
"field_name": "test_select",
23+
"field_label": "Test Select",
24+
"field_select": "select",
25+
"field_required": False,
26+
}
27+
defaults.update(config_kwargs)
28+
29+
return add_plugin(
30+
placeholder=self.placeholder,
31+
plugin_type="SelectPlugin",
32+
language=self.language,
33+
config=defaults,
34+
)
35+
36+
def _create_choice_plugin(self, parent, value, verbose):
37+
"""Helper to create a ChoicePlugin as child of SelectPlugin"""
38+
return add_plugin(
39+
placeholder=self.placeholder,
40+
plugin_type="ChoicePlugin",
41+
language=self.language,
42+
target=parent,
43+
config={"value": value, "verbose": verbose},
44+
)
45+
46+
def _get_form_with_choices(self, instance, choices_data):
47+
"""Helper to get admin form with field_choices populated
48+
49+
choices_data should be a list of (value, verbose) tuples.
50+
The ChoicesFormField uses AttributesWidget which expects POST data
51+
with attributes_key[field_name] and attributes_value[field_name] lists.
52+
We need to use a QueryDict to provide getlist() method.
53+
"""
54+
form_class = self.plugin_class.form
55+
56+
# Build a QueryDict with the form data
57+
query_string_parts = [
58+
f"field_name={instance.config.get('field_name', 'test')}",
59+
f"field_label={instance.config.get('field_label', 'Test')}",
60+
f"field_select={instance.config.get('field_select', 'select')}",
61+
f"field_required={instance.config.get('field_required', False)}",
62+
]
63+
64+
# Add choices in the AttributesWidget format
65+
for value, verbose in choices_data:
66+
query_string_parts.append(f"attributes_key[field_choices]={value}")
67+
query_string_parts.append(f"attributes_value[field_choices]={verbose}")
68+
69+
query_string = "&".join(query_string_parts)
70+
data = QueryDict(query_string, mutable=False)
71+
72+
form = form_class(data=data, instance=instance)
73+
return form
74+
75+
def test_select_plugin_registered(self):
76+
"""Verify SelectPlugin is properly registered"""
77+
self.assertIsNotNone(self.plugin_class)
78+
self.assertEqual(self.plugin_class.name, "Select")
79+
self.assertEqual(self.plugin_class.model, Select)
80+
81+
def test_select_plugin_allows_choice_children(self):
82+
"""Verify SelectPlugin allows ChoicePlugin children"""
83+
self.assertTrue(self.plugin_class.allow_children)
84+
self.assertIn("ChoicePlugin", self.plugin_class.child_classes)
85+
86+
def test_save_model_adds_new_choices(self):
87+
"""Test that save_model adds new ChoicePlugin instances from field_choices"""
88+
select_instance = self._create_select_plugin()
89+
90+
# Initially no children
91+
self.assertEqual(select_instance.get_children().count(), 0)
92+
93+
# Create form with new choices
94+
choices_data = [
95+
("val1", "Option 1"),
96+
("val2", "Option 2"),
97+
("val3", "Option 3"),
98+
]
99+
form = self._get_form_with_choices(select_instance, choices_data)
100+
self.assertTrue(form.is_valid(), form.errors)
101+
102+
# Save via plugin's save_model
103+
plugin_instance = SelectPlugin(self.plugin_class.model, self.admin_site)
104+
plugin_instance.save_model(
105+
request=self.get_request("/"),
106+
obj=select_instance,
107+
form=form,
108+
change=True,
109+
)
110+
111+
# Verify children were created
112+
children = select_instance.get_children()
113+
self.assertEqual(children.count(), 3)
114+
115+
# Verify choice values and verbose names
116+
choice_plugins = [child.djangocms_form_builder_formfield for child in children]
117+
actual_choices = [
118+
(c.config["value"], c.config["verbose"]) for c in choice_plugins
119+
]
120+
self.assertEqual(sorted(actual_choices), sorted(choices_data))
121+
122+
def test_save_model_updates_existing_choices(self):
123+
"""Test that save_model updates existing ChoicePlugin verbose names"""
124+
select_instance = self._create_select_plugin()
125+
126+
# Add initial choices
127+
self._create_choice_plugin(select_instance, "opt1", "Original 1")
128+
self._create_choice_plugin(select_instance, "opt2", "Original 2")
129+
130+
initial_count = select_instance.get_children().count()
131+
self.assertEqual(initial_count, 2)
132+
133+
# Update choices via form (same values, different verbose names)
134+
choices_data = [("opt1", "Updated 1"), ("opt2", "Updated 2")]
135+
form = self._get_form_with_choices(select_instance, choices_data)
136+
self.assertTrue(form.is_valid(), form.errors)
137+
138+
plugin_instance = SelectPlugin(self.plugin_class.model, self.admin_site)
139+
plugin_instance.save_model(
140+
request=self.get_request("/"),
141+
obj=select_instance,
142+
form=form,
143+
change=True,
144+
)
145+
146+
# Verify count stays the same
147+
self.assertEqual(select_instance.get_children().count(), initial_count)
148+
149+
# Verify verbose names were updated
150+
children = select_instance.get_children()
151+
choice_plugins = [child.djangocms_form_builder_formfield for child in children]
152+
choices_dict = {c.config["value"]: c.config["verbose"] for c in choice_plugins}
153+
self.assertEqual(choices_dict["opt1"], "Updated 1")
154+
self.assertEqual(choices_dict["opt2"], "Updated 2")
155+
156+
def test_save_model_deletes_removed_choices(self):
157+
"""Test that save_model deletes ChoicePlugin instances not in field_choices"""
158+
select_instance = self._create_select_plugin()
159+
160+
# Add initial choices
161+
self._create_choice_plugin(select_instance, "keep", "Keep This")
162+
self._create_choice_plugin(select_instance, "remove1", "Remove 1")
163+
self._create_choice_plugin(select_instance, "remove2", "Remove 2")
164+
165+
self.assertEqual(select_instance.get_children().count(), 3)
166+
167+
# Update to only keep one choice
168+
choices_data = [("keep", "Keep This")]
169+
form = self._get_form_with_choices(select_instance, choices_data)
170+
self.assertTrue(form.is_valid(), form.errors)
171+
172+
plugin_instance = SelectPlugin(self.plugin_class.model, self.admin_site)
173+
plugin_instance.save_model(
174+
request=self.get_request("/"),
175+
obj=select_instance,
176+
form=form,
177+
change=True,
178+
)
179+
180+
# Verify only one child remains
181+
children = select_instance.get_children()
182+
self.assertEqual(children.count(), 1)
183+
184+
# Verify it's the correct one
185+
remaining = children.first().djangocms_form_builder_formfield
186+
self.assertEqual(remaining.config["value"], "keep")
187+
self.assertEqual(remaining.config["verbose"], "Keep This")
188+
189+
def test_save_model_mixed_operations(self):
190+
"""Test save_model with add, update, and delete in one operation"""
191+
select_instance = self._create_select_plugin()
192+
193+
# Add initial choices
194+
self._create_choice_plugin(select_instance, "update", "Original")
195+
self._create_choice_plugin(select_instance, "delete", "Will Delete")
196+
197+
self.assertEqual(select_instance.get_children().count(), 2)
198+
199+
# Mixed operation: update "update", delete "delete", add "new"
200+
choices_data = [
201+
("update", "Updated Value"),
202+
("new", "Newly Added"),
203+
]
204+
form = self._get_form_with_choices(select_instance, choices_data)
205+
self.assertTrue(form.is_valid(), form.errors)
206+
207+
plugin_instance = SelectPlugin(self.plugin_class.model, self.admin_site)
208+
plugin_instance.save_model(
209+
request=self.get_request("/"),
210+
obj=select_instance,
211+
form=form,
212+
change=True,
213+
)
214+
215+
# Verify count: 2 original - 1 deleted + 1 added = 2
216+
children = select_instance.get_children()
217+
self.assertEqual(children.count(), 2)
218+
219+
# Verify the correct choices remain
220+
choice_plugins = [child.djangocms_form_builder_formfield for child in children]
221+
choices_dict = {c.config["value"]: c.config["verbose"] for c in choice_plugins}
222+
223+
self.assertIn("update", choices_dict)
224+
self.assertEqual(choices_dict["update"], "Updated Value")
225+
self.assertIn("new", choices_dict)
226+
self.assertEqual(choices_dict["new"], "Newly Added")
227+
self.assertNotIn("delete", choices_dict)
228+
229+
def test_save_model_preserves_choice_order(self):
230+
"""Test that choices maintain their order after updates"""
231+
select_instance = self._create_select_plugin()
232+
233+
# Add initial choices in specific order
234+
choices_data = [("a", "Alpha"), ("b", "Beta"), ("c", "Gamma")]
235+
form = self._get_form_with_choices(select_instance, choices_data)
236+
self.assertTrue(form.is_valid(), form.errors)
237+
238+
plugin_instance = SelectPlugin(self.plugin_class.model, self.admin_site)
239+
plugin_instance.save_model(
240+
request=self.get_request("/"),
241+
obj=select_instance,
242+
form=form,
243+
change=True,
244+
)
245+
246+
# Refresh the instance to clear cached choices
247+
select_instance.refresh_from_db()
248+
select_instance._choices = None # Clear the cached choices
249+
250+
# Get choices via the model's get_choices method
251+
actual_choices = select_instance.get_choices()
252+
expected_choices = [("a", "Alpha"), ("b", "Beta"), ("c", "Gamma")]
253+
self.assertEqual(actual_choices, expected_choices)
254+
255+
def test_save_model_no_changes_to_choices(self):
256+
"""Test that save_model handles unchanged choices correctly"""
257+
select_instance = self._create_select_plugin()
258+
259+
# Add initial choices
260+
choice1 = self._create_choice_plugin(select_instance, "same1", "Same 1")
261+
choice2 = self._create_choice_plugin(select_instance, "same2", "Same 2")
262+
263+
initial_ids = [choice1.pk, choice2.pk]
264+
265+
# Re-save with same choices
266+
choices_data = [("same1", "Same 1"), ("same2", "Same 2")]
267+
form = self._get_form_with_choices(select_instance, choices_data)
268+
self.assertTrue(form.is_valid(), form.errors)
269+
270+
plugin_instance = SelectPlugin(self.plugin_class.model, self.admin_site)
271+
plugin_instance.save_model(
272+
request=self.get_request("/"),
273+
obj=select_instance,
274+
form=form,
275+
change=True,
276+
)
277+
278+
# Verify same instances remain (IDs unchanged)
279+
children = select_instance.get_children()
280+
self.assertEqual(children.count(), 2)
281+
282+
# IDs should be the same
283+
current_ids = sorted([child.pk for child in children])
284+
self.assertEqual(sorted(initial_ids), current_ids)
285+
286+
def test_save_model_empty_choices(self):
287+
"""Test that ChoicesFormField validation requires at least one choice"""
288+
select_instance = self._create_select_plugin()
289+
290+
# Add initial choices
291+
self._create_choice_plugin(select_instance, "remove1", "Remove 1")
292+
self._create_choice_plugin(select_instance, "remove2", "Remove 2")
293+
294+
self.assertEqual(select_instance.get_children().count(), 2)
295+
296+
# Try to save with empty choices - should fail validation
297+
choices_data = []
298+
form = self._get_form_with_choices(select_instance, choices_data)
299+
300+
# The form should NOT be valid with empty choices
301+
self.assertFalse(form.is_valid())
302+
self.assertIn("field_choices", form.errors)
303+
304+
def test_get_choices_returns_ordered_list(self):
305+
"""Test that Select.get_choices() returns choices in correct order"""
306+
select_instance = self._create_select_plugin()
307+
308+
# Add choices in specific order
309+
self._create_choice_plugin(select_instance, "first", "First Choice")
310+
self._create_choice_plugin(select_instance, "second", "Second Choice")
311+
self._create_choice_plugin(select_instance, "third", "Third Choice")
312+
313+
choices = select_instance.get_choices()
314+
315+
self.assertEqual(len(choices), 3)
316+
self.assertEqual(choices[0], ("first", "First Choice"))
317+
self.assertEqual(choices[1], ("second", "Second Choice"))
318+
self.assertEqual(choices[2], ("third", "Third Choice"))
319+
320+
def test_select_field_form_initializes_with_existing_choices(self):
321+
"""Test that SelectFieldForm populates field_choices from instance"""
322+
select_instance = self._create_select_plugin()
323+
324+
# Add some choices
325+
self._create_choice_plugin(select_instance, "opt1", "Option 1")
326+
self._create_choice_plugin(select_instance, "opt2", "Option 2")
327+
328+
# Create form with instance
329+
form_class = self.plugin_class.form
330+
form = form_class(instance=select_instance)
331+
332+
# field_choices should be initialized with existing choices
333+
initial_choices = form.fields["field_choices"].initial
334+
self.assertEqual(len(initial_choices), 2)
335+
self.assertIn(("opt1", "Option 1"), initial_choices)
336+
self.assertIn(("opt2", "Option 2"), initial_choices)

0 commit comments

Comments
 (0)