eea.plotly 2.2__tar.gz → 2.3__tar.gz

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.
Files changed (138) hide show
  1. {eea_plotly-2.2/eea.plotly.egg-info → eea_plotly-2.3}/PKG-INFO +6 -1
  2. {eea_plotly-2.2 → eea_plotly-2.3}/docs/HISTORY.txt +5 -0
  3. eea_plotly-2.3/eea/plotly/tests/test_utils.py +281 -0
  4. eea_plotly-2.3/eea/plotly/version.txt +1 -0
  5. {eea_plotly-2.2 → eea_plotly-2.3/eea.plotly.egg-info}/PKG-INFO +6 -1
  6. {eea_plotly-2.2 → eea_plotly-2.3}/eea.plotly.egg-info/SOURCES.txt +1 -0
  7. eea_plotly-2.2/eea/plotly/version.txt +0 -1
  8. {eea_plotly-2.2 → eea_plotly-2.3}/CONTRIBUTING.md +0 -0
  9. {eea_plotly-2.2 → eea_plotly-2.3}/MANIFEST.in +0 -0
  10. {eea_plotly-2.2 → eea_plotly-2.3}/README.rst +0 -0
  11. {eea_plotly-2.2 → eea_plotly-2.3}/docs/LICENSE.GPL +0 -0
  12. {eea_plotly-2.2 → eea_plotly-2.3}/docs/LICENSE.txt +0 -0
  13. {eea_plotly-2.2 → eea_plotly-2.3}/eea/__init__.py +0 -0
  14. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/README.txt +0 -0
  15. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/__init__.py +0 -0
  16. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/behaviors.py +0 -0
  17. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/browser/__init__.py +0 -0
  18. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/browser/configure.zcml +0 -0
  19. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/browser/controlpanel.py +0 -0
  20. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/browser/preview.py +0 -0
  21. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/browser/preview_adapter/adapter.py +0 -0
  22. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/browser/preview_adapter/soer_miniature.py +0 -0
  23. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/browser/pt/visualization_view.pt +0 -0
  24. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/configure.zcml +0 -0
  25. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/controlpanel.py +0 -0
  26. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/deserializer/blocks.py +0 -0
  27. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/deserializer/configure.zcml +0 -0
  28. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/deserializer/dxcontent.py +0 -0
  29. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/deserializer/dxfields.py +0 -0
  30. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/interfaces.py +0 -0
  31. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/io_csv.py +0 -0
  32. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/io_json.py +0 -0
  33. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/__init__.py +0 -0
  34. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/bg/LC_MESSAGES/eea.po +0 -0
  35. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/bg/LC_MESSAGES/plone.po +0 -0
  36. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/cs/LC_MESSAGES/eea.po +0 -0
  37. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/cs/LC_MESSAGES/plone.po +0 -0
  38. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/da/LC_MESSAGES/eea.po +0 -0
  39. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/da/LC_MESSAGES/plone.po +0 -0
  40. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/de/LC_MESSAGES/eea.po +0 -0
  41. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/de/LC_MESSAGES/plone.po +0 -0
  42. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/eea.pot +0 -0
  43. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/el/LC_MESSAGES/eea.po +0 -0
  44. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/el/LC_MESSAGES/plone.po +0 -0
  45. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/en/LC_MESSAGES/eea.po +0 -0
  46. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/en/LC_MESSAGES/plone.po +0 -0
  47. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/es/LC_MESSAGES/eea.po +0 -0
  48. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/es/LC_MESSAGES/plone.po +0 -0
  49. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/et/LC_MESSAGES/eea.po +0 -0
  50. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/et/LC_MESSAGES/plone.po +0 -0
  51. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/eu/LC_MESSAGES/eea.po +0 -0
  52. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/eu/LC_MESSAGES/plone.po +0 -0
  53. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/fi/LC_MESSAGES/eea.po +0 -0
  54. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/fi/LC_MESSAGES/plone.po +0 -0
  55. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/fr/LC_MESSAGES/eea.po +0 -0
  56. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/fr/LC_MESSAGES/plone.po +0 -0
  57. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/hr/LC_MESSAGES/eea.po +0 -0
  58. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/hr/LC_MESSAGES/plone.po +0 -0
  59. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/hu/LC_MESSAGES/eea.po +0 -0
  60. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/hu/LC_MESSAGES/plone.po +0 -0
  61. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/is/LC_MESSAGES/eea.po +0 -0
  62. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/is/LC_MESSAGES/plone.po +0 -0
  63. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/it/LC_MESSAGES/eea.po +0 -0
  64. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/it/LC_MESSAGES/plone.po +0 -0
  65. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/kl/LC_MESSAGES/eea.po +0 -0
  66. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/kl/LC_MESSAGES/plone.po +0 -0
  67. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/lt/LC_MESSAGES/eea.po +0 -0
  68. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/lt/LC_MESSAGES/plone.po +0 -0
  69. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/lv/LC_MESSAGES/eea.po +0 -0
  70. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/lv/LC_MESSAGES/plone.po +0 -0
  71. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/mt/LC_MESSAGES/eea.po +0 -0
  72. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/mt/LC_MESSAGES/plone.po +0 -0
  73. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/nl/LC_MESSAGES/eea.po +0 -0
  74. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/nl/LC_MESSAGES/plone.po +0 -0
  75. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/no/LC_MESSAGES/eea.po +0 -0
  76. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/no/LC_MESSAGES/plone.po +0 -0
  77. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/pl/LC_MESSAGES/eea.po +0 -0
  78. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/pl/LC_MESSAGES/plone.po +0 -0
  79. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/plone-manual.pot +0 -0
  80. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/pt/LC_MESSAGES/eea.po +0 -0
  81. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/pt/LC_MESSAGES/plone.po +0 -0
  82. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/pt_BR/LC_MESSAGES/eea.po +0 -0
  83. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/pt_BR/LC_MESSAGES/plone.po +0 -0
  84. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/ro/LC_MESSAGES/eea.po +0 -0
  85. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/ro/LC_MESSAGES/plone.po +0 -0
  86. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/ru/LC_MESSAGES/eea.po +0 -0
  87. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/ru/LC_MESSAGES/plone.po +0 -0
  88. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/sk/LC_MESSAGES/eea.po +0 -0
  89. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/sk/LC_MESSAGES/plone.po +0 -0
  90. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/sl/LC_MESSAGES/eea.po +0 -0
  91. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/sl/LC_MESSAGES/plone.po +0 -0
  92. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/sv/LC_MESSAGES/eea.po +0 -0
  93. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/sv/LC_MESSAGES/plone.po +0 -0
  94. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/tr/LC_MESSAGES/eea.po +0 -0
  95. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/tr/LC_MESSAGES/plone.po +0 -0
  96. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/update.sh +0 -0
  97. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/zh_TW/LC_MESSAGES/eea.po +0 -0
  98. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/locales/zh_TW/LC_MESSAGES/plone.po +0 -0
  99. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/permissions.zcml +0 -0
  100. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/browserlayer.xml +0 -0
  101. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/controlpanel.json +0 -0
  102. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/controlpanel.xml +0 -0
  103. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/metadata.xml +0 -0
  104. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/registry.xml +0 -0
  105. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/rolemap.xml +0 -0
  106. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/types/visualization.xml +0 -0
  107. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/default/types.xml +0 -0
  108. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/uninstall/browserlayer.xml +0 -0
  109. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/uninstall/controlpanel.xml +0 -0
  110. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles/uninstall/registry.xml +0 -0
  111. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/profiles.zcml +0 -0
  112. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/__init__.py +0 -0
  113. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/configure.zcml +0 -0
  114. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/controlpanel/__init__.py +0 -0
  115. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/controlpanel/configure.zcml +0 -0
  116. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/controlpanel/get.py +0 -0
  117. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/controlpanel/reset.py +0 -0
  118. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/visualization/configure.zcml +0 -0
  119. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/restapi/visualization/get.py +0 -0
  120. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/serializer/blocks.py +0 -0
  121. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/serializer/configure.zcml +0 -0
  122. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/serializer/dxfields.py +0 -0
  123. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/serializer/utils.py +0 -0
  124. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/setuphandlers.py +0 -0
  125. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/tests/__init__.py +0 -0
  126. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/tests/base.py +0 -0
  127. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/tests/test_doctests.py +0 -0
  128. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/upgrades/__init__.py +0 -0
  129. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/upgrades/configure.zcml +0 -0
  130. {eea_plotly-2.2 → eea_plotly-2.3}/eea/plotly/utils.py +0 -0
  131. {eea_plotly-2.2 → eea_plotly-2.3}/eea.plotly.egg-info/dependency_links.txt +0 -0
  132. {eea_plotly-2.2 → eea_plotly-2.3}/eea.plotly.egg-info/entry_points.txt +0 -0
  133. {eea_plotly-2.2 → eea_plotly-2.3}/eea.plotly.egg-info/namespace_packages.txt +0 -0
  134. {eea_plotly-2.2 → eea_plotly-2.3}/eea.plotly.egg-info/not-zip-safe +0 -0
  135. {eea_plotly-2.2 → eea_plotly-2.3}/eea.plotly.egg-info/requires.txt +0 -0
  136. {eea_plotly-2.2 → eea_plotly-2.3}/eea.plotly.egg-info/top_level.txt +0 -0
  137. {eea_plotly-2.2 → eea_plotly-2.3}/setup.cfg +0 -0
  138. {eea_plotly-2.2 → eea_plotly-2.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eea.plotly
3
- Version: 2.2
3
+ Version: 2.3
4
4
  Summary: volto-plotlycharts backend support
5
5
  Home-page: https://github.com/eea/eea.plotly
6
6
  Author: European Environment Agency: IDM2 A-Team
@@ -145,6 +145,11 @@ EEA_ - European Environment Agency (EU)
145
145
  Changelog
146
146
  =========
147
147
 
148
+ 2.3 - (2026-04-14)
149
+ ---------------------------
150
+ * Change: Add unit tests for utils, io_csv, and io_json modules
151
+ [avoinea]
152
+
148
153
  2.2 - (2025-11-20)
149
154
  ---------------------------
150
155
  * Change: better handle of backward compatibility, ref #293767
@@ -1,6 +1,11 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 2.3 - (2026-04-14)
5
+ ---------------------------
6
+ * Change: Add unit tests for utils, io_csv, and io_json modules
7
+ [avoinea]
8
+
4
9
  2.2 - (2025-11-20)
5
10
  ---------------------------
6
11
  * Change: better handle of backward compatibility, ref #293767
@@ -0,0 +1,281 @@
1
+ """Unit tests for eea.plotly utility modules
2
+
3
+ These tests cover pure functions that don't require Plone context:
4
+ - utils: setProperty, delProperty, sanitizeVisualization, getLink, getLinkHTML, isExpanded
5
+ - io_csv: CsvWriter, CsvReader
6
+ - io_json: JSONEncoder
7
+ """
8
+
9
+ import unittest
10
+ import json
11
+ import decimal
12
+ import io
13
+
14
+ from eea.plotly.utils import (
15
+ setProperty,
16
+ delProperty,
17
+ sanitizeVisualization,
18
+ getLink,
19
+ getLinkHTML,
20
+ isExpanded,
21
+ )
22
+ from eea.plotly.io_csv import CsvWriter, CsvReader, NULL
23
+ from eea.plotly.io_json import JSONEncoder
24
+
25
+
26
+ class TestSetProperty(unittest.TestCase):
27
+ """Tests for setProperty function"""
28
+
29
+ def test_set_value_not_none(self):
30
+ obj = {}
31
+ setProperty(obj, "key", "value")
32
+ self.assertEqual(obj["key"], "value")
33
+
34
+ def test_set_value_none(self):
35
+ obj = {}
36
+ setProperty(obj, "key", None)
37
+ self.assertNotIn("key", obj)
38
+
39
+ def test_set_overwrites(self):
40
+ obj = {"key": "old"}
41
+ setProperty(obj, "key", "new")
42
+ self.assertEqual(obj["key"], "new")
43
+
44
+ def test_set_integer(self):
45
+ obj = {}
46
+ setProperty(obj, "count", 42)
47
+ self.assertEqual(obj["count"], 42)
48
+
49
+ def test_set_empty_string(self):
50
+ obj = {}
51
+ setProperty(obj, "key", "")
52
+ self.assertEqual(obj["key"], "")
53
+
54
+
55
+ class TestDelProperty(unittest.TestCase):
56
+ """Tests for delProperty function"""
57
+
58
+ def test_delete_existing(self):
59
+ obj = {"key": "value"}
60
+ delProperty(obj, "key")
61
+ self.assertNotIn("key", obj)
62
+
63
+ def test_delete_nonexistent(self):
64
+ obj = {}
65
+ delProperty(obj, "key") # should not raise
66
+
67
+ def test_delete_other_keys_preserved(self):
68
+ obj = {"a": 1, "b": 2}
69
+ delProperty(obj, "a")
70
+ self.assertEqual(obj, {"b": 2})
71
+
72
+
73
+ class TestSanitizeVisualization(unittest.TestCase):
74
+ """Tests for sanitizeVisualization function"""
75
+
76
+ def test_empty_visualization(self):
77
+ result = sanitizeVisualization({})
78
+ self.assertEqual(result["data"], [])
79
+ self.assertEqual(result["layout"], {})
80
+ self.assertEqual(result["columns"], [])
81
+ self.assertEqual(result["dataSources"], {})
82
+
83
+ def test_with_data(self):
84
+ v = {"data": [{"x": [1, 2], "y": [3, 4]}]}
85
+ result = sanitizeVisualization(v)
86
+ self.assertEqual(result["data"], [{"x": [1, 2], "y": [3, 4]}])
87
+
88
+ def test_with_chart_data_backward_compat(self):
89
+ v = {"chartData": {"data": [{"x": [1]}], "layout": {"title": "test"}}}
90
+ result = sanitizeVisualization(v)
91
+ self.assertEqual(result["data"], [{"x": [1]}])
92
+ self.assertEqual(result["layout"], {"title": "test"})
93
+
94
+ def test_with_layout(self):
95
+ v = {"layout": {"title": "My Chart"}}
96
+ result = sanitizeVisualization(v)
97
+ self.assertEqual(result["layout"], {"title": "My Chart"})
98
+
99
+ def test_with_columns(self):
100
+ v = {"columns": [{"name": "col1"}]}
101
+ result = sanitizeVisualization(v)
102
+ self.assertEqual(result["columns"], [{"name": "col1"}])
103
+
104
+ def test_data_sources_merge(self):
105
+ v = {
106
+ "data_source": {"src1": "url1"},
107
+ "dataSources": {"src2": "url2"},
108
+ }
109
+ result = sanitizeVisualization(v)
110
+ self.assertEqual(result["dataSources"], {"src1": "url1", "src2": "url2"})
111
+
112
+ def test_optional_properties_set(self):
113
+ v = {
114
+ "provider_url": "https://example.com",
115
+ "variation": "bar",
116
+ "filters": {"field": "value"},
117
+ "id": "viz-1",
118
+ "type": "plotly",
119
+ "label": "My Viz",
120
+ }
121
+ result = sanitizeVisualization(v)
122
+ self.assertEqual(result["provider_url"], "https://example.com")
123
+ self.assertEqual(result["variation"], "bar")
124
+ self.assertEqual(result["filters"], {"field": "value"})
125
+ self.assertEqual(result["id"], "viz-1")
126
+ self.assertEqual(result["type"], "plotly")
127
+ self.assertEqual(result["label"], "My Viz")
128
+
129
+ def test_optional_properties_none_not_set(self):
130
+ v = {"provider_url": None, "variation": None}
131
+ result = sanitizeVisualization(v)
132
+ self.assertNotIn("provider_url", result)
133
+ self.assertNotIn("variation", result)
134
+
135
+ def test_default_empty_dict(self):
136
+ result = sanitizeVisualization()
137
+ self.assertIsNotNone(result)
138
+
139
+
140
+ class TestGetLink(unittest.TestCase):
141
+ """Tests for getLink function"""
142
+
143
+ def test_localhost_link(self):
144
+ result = getLink("http://localhost:8080/Plone/page")
145
+ self.assertEqual(result, "/Plone/page")
146
+
147
+ def test_external_link(self):
148
+ result = getLink("https://example.com/page")
149
+ self.assertEqual(result, "https://example.com/page")
150
+
151
+ def test_relative_path(self):
152
+ result = getLink("/Plone/page")
153
+ self.assertEqual(result, "/Plone/page")
154
+
155
+
156
+ class TestGetLinkHTML(unittest.TestCase):
157
+ """Tests for getLinkHTML function"""
158
+
159
+ def test_with_text(self):
160
+ result = getLinkHTML("https://example.com", "Click here")
161
+ self.assertEqual(
162
+ result, '<a href="https://example.com" target="_blank">Click here</a>'
163
+ )
164
+
165
+ def test_without_text(self):
166
+ result = getLinkHTML("https://example.com")
167
+ self.assertEqual(
168
+ result,
169
+ '<a href="https://example.com" target="_blank">https://example.com</a>',
170
+ )
171
+
172
+ def test_empty_url(self):
173
+ result = getLinkHTML("")
174
+ self.assertEqual(result, "")
175
+
176
+ def test_none_url(self):
177
+ result = getLinkHTML(None)
178
+ self.assertIsNone(result)
179
+
180
+
181
+ class TestIsExpanded(unittest.TestCase):
182
+ """Tests for isExpanded function"""
183
+
184
+ def test_expanded(self):
185
+ class FakeRequest:
186
+ form = {"expand.visualization": "1"}
187
+
188
+ self.assertTrue(isExpanded(FakeRequest()))
189
+
190
+ def test_not_expanded(self):
191
+ class FakeRequest:
192
+ form = {}
193
+
194
+ self.assertFalse(isExpanded(FakeRequest()))
195
+
196
+
197
+ class TestCsvWriter(unittest.TestCase):
198
+ """Tests for CsvWriter class"""
199
+
200
+ def test_write_row_with_none(self):
201
+ output = io.StringIO()
202
+ writer = CsvWriter(output)
203
+ writer.writerow(["a", None, "b"])
204
+ output.seek(0)
205
+ self.assertEqual(output.read().strip(), "a,NULL,b")
206
+
207
+ def test_write_row_no_none(self):
208
+ output = io.StringIO()
209
+ writer = CsvWriter(output)
210
+ writer.writerow(["a", "b", "c"])
211
+ output.seek(0)
212
+ self.assertEqual(output.read().strip(), "a,b,c")
213
+
214
+ def test_write_rows(self):
215
+ output = io.StringIO()
216
+ writer = CsvWriter(output)
217
+ writer.writerows([["a", None], [None, "b"]])
218
+ output.seek(0)
219
+ lines = output.read().strip().split("\n")
220
+ self.assertEqual(lines[0].strip(), "a,NULL")
221
+ self.assertEqual(lines[1].strip(), "NULL,b")
222
+
223
+ def test_write_empty_row(self):
224
+ output = io.StringIO()
225
+ writer = CsvWriter(output)
226
+ writer.writerow([])
227
+ output.seek(0)
228
+ self.assertEqual(output.read().strip(), "")
229
+
230
+
231
+ class TestCsvReader(unittest.TestCase):
232
+ """Tests for CsvReader class"""
233
+
234
+ def test_read_row_with_null(self):
235
+ input_data = "a,NULL,b\n"
236
+ reader = CsvReader(io.StringIO(input_data))
237
+ rows = list(reader)
238
+ self.assertEqual(rows, [["a", None, "b"]])
239
+
240
+ def test_read_row_no_null(self):
241
+ input_data = "a,b,c\n"
242
+ reader = CsvReader(io.StringIO(input_data))
243
+ rows = list(reader)
244
+ self.assertEqual(rows, [["a", "b", "c"]])
245
+
246
+ def test_read_multiple_rows(self):
247
+ input_data = "a,b\nNULL,c\n"
248
+ reader = CsvReader(io.StringIO(input_data))
249
+ rows = list(reader)
250
+ self.assertEqual(len(rows), 2)
251
+ self.assertEqual(rows[0], ["a", "b"])
252
+ self.assertEqual(rows[1], [None, "c"])
253
+
254
+ def test_null_constant(self):
255
+ self.assertEqual(NULL, "NULL")
256
+
257
+
258
+ class TestJSONEncoder(unittest.TestCase):
259
+ """Tests for JSONEncoder class"""
260
+
261
+ def test_encode_decimal(self):
262
+ result = json.dumps({"value": decimal.Decimal("3.14")}, cls=JSONEncoder)
263
+ self.assertEqual(result, '{"value": "3.14"}')
264
+
265
+ def test_encode_regular_types(self):
266
+ result = json.dumps({"int": 42, "str": "hello"}, cls=JSONEncoder)
267
+ self.assertEqual(result, '{"int": 42, "str": "hello"}')
268
+
269
+ def test_encode_nested_decimal(self):
270
+ data = {"chart": {"value": decimal.Decimal("10.5")}}
271
+ result = json.dumps(data, cls=JSONEncoder)
272
+ self.assertEqual(result, '{"chart": {"value": "10.5"}}')
273
+
274
+ def test_encode_list_with_decimal(self):
275
+ data = [1, decimal.Decimal("2.5"), 3]
276
+ result = json.dumps(data, cls=JSONEncoder)
277
+ self.assertEqual(result, '[1, "2.5", 3]')
278
+
279
+
280
+ if __name__ == "__main__":
281
+ unittest.main()
@@ -0,0 +1 @@
1
+ 2.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eea.plotly
3
- Version: 2.2
3
+ Version: 2.3
4
4
  Summary: volto-plotlycharts backend support
5
5
  Home-page: https://github.com/eea/eea.plotly
6
6
  Author: European Environment Agency: IDM2 A-Team
@@ -145,6 +145,11 @@ EEA_ - European Environment Agency (EU)
145
145
  Changelog
146
146
  =========
147
147
 
148
+ 2.3 - (2026-04-14)
149
+ ---------------------------
150
+ * Change: Add unit tests for utils, io_csv, and io_json modules
151
+ [avoinea]
152
+
148
153
  2.2 - (2025-11-20)
149
154
  ---------------------------
150
155
  * Change: better handle of backward compatibility, ref #293767
@@ -130,5 +130,6 @@ eea/plotly/serializer/utils.py
130
130
  eea/plotly/tests/__init__.py
131
131
  eea/plotly/tests/base.py
132
132
  eea/plotly/tests/test_doctests.py
133
+ eea/plotly/tests/test_utils.py
133
134
  eea/plotly/upgrades/__init__.py
134
135
  eea/plotly/upgrades/configure.zcml
@@ -1 +0,0 @@
1
- 2.2
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes