|
1 | 1 | """Unit tests for callback decorator behavior - no browser required.""" |
2 | 2 | import inspect |
| 3 | +import json |
3 | 4 |
|
4 | 5 | import dash |
5 | 6 | from dash import Input, Output, State, callback |
| 7 | +from dash._utils import create_callback_id |
6 | 8 |
|
7 | 9 |
|
8 | 10 | def test_callback_returns_callable(): |
@@ -126,3 +128,38 @@ def test_callback_module_export(): |
126 | 128 | """Test that callback is properly exported from dash module.""" |
127 | 129 | assert hasattr(dash, "callback") |
128 | 130 | assert dash.callback is callback |
| 131 | + |
| 132 | + |
| 133 | +def test_create_callback_id_escapes_dots_in_string_id(): |
| 134 | + """A dot in a plain string component id is escaped with a backslash.""" |
| 135 | + output = Output("my.component", "children") |
| 136 | + callback_id = create_callback_id(output, []) |
| 137 | + |
| 138 | + assert callback_id == "my\\.component.children" |
| 139 | + |
| 140 | + |
| 141 | +def test_create_callback_id_escapes_dots_in_dict_id_as_json_unicode(): |
| 142 | + """A dot in a dict id must use the JSON \\u002e escape, not \\., |
| 143 | + otherwise the frontend's JSON.parse throws a SyntaxError when it |
| 144 | + un-escapes the id portion of the callback id string (see #3480).""" |
| 145 | + output = Output({"type": "my.type", "index": 1}, "children") |
| 146 | + callback_id = create_callback_id(output, []) |
| 147 | + |
| 148 | + id_part, prop_part = callback_id.rsplit(".", 1) |
| 149 | + assert prop_part == "children" |
| 150 | + # The escaped id must not contain a raw backslash-dot sequence... |
| 151 | + assert "\\." not in id_part |
| 152 | + # ...and must be valid JSON once the . escape is present verbatim. |
| 153 | + assert "\\u002e" in id_part |
| 154 | + parsed = json.loads(id_part) |
| 155 | + assert parsed == {"type": "my.type", "index": 1} |
| 156 | + |
| 157 | + |
| 158 | +def test_create_callback_id_dict_id_without_dots_unaffected(): |
| 159 | + """Dict ids with no dots in their values still round-trip through JSON.""" |
| 160 | + output = Output({"type": "widget", "index": 2}, "value") |
| 161 | + callback_id = create_callback_id(output, []) |
| 162 | + |
| 163 | + id_part, prop_part = callback_id.rsplit(".", 1) |
| 164 | + assert prop_part == "value" |
| 165 | + assert json.loads(id_part) == {"type": "widget", "index": 2} |
0 commit comments