Navegador

navegador / tests / test_enrichment_react_native.py
Blame History Raw 262 lines
1
"""Tests for navegador.enrichment.react_native — ReactNativeEnricher."""
2
3
from unittest.mock import MagicMock
4
5
import pytest
6
7
from navegador.enrichment import EnrichmentResult
8
from navegador.enrichment.react_native import ReactNativeEnricher
9
from navegador.graph.store import GraphStore
10
11
12
# ── Helpers ───────────────────────────────────────────────────────────────────
13
14
15
def _mock_store(result_set=None):
16
client = MagicMock()
17
graph = MagicMock()
18
graph.query.return_value = MagicMock(result_set=result_set)
19
client.select_graph.return_value = graph
20
return GraphStore(client)
21
22
23
def _mock_store_with_responses(responses):
24
"""Return a GraphStore whose graph.query returns the given result sets in order.
25
26
Any calls beyond the provided list (e.g. _promote_node SET queries) receive
27
a MagicMock with result_set=None, which is harmless.
28
"""
29
client = MagicMock()
30
graph = MagicMock()
31
response_iter = iter(responses)
32
33
def _side_effect(cypher, params):
34
try:
35
rs = next(response_iter)
36
except StopIteration:
37
rs = None
38
return MagicMock(result_set=rs)
39
40
graph.query.side_effect = _side_effect
41
client.select_graph.return_value = graph
42
return GraphStore(client)
43
44
45
# ── framework_name ────────────────────────────────────────────────────────────
46
47
48
class TestFrameworkName:
49
def test_framework_name_is_react_native(self):
50
enricher = ReactNativeEnricher(_mock_store())
51
assert enricher.framework_name == "react-native"
52
53
54
# ── detection_patterns ────────────────────────────────────────────────────────
55
56
57
class TestDetectionPatterns:
58
def test_contains_react_native_hyphenated(self):
59
enricher = ReactNativeEnricher(_mock_store())
60
assert "react-native" in enricher.detection_patterns
61
62
def test_contains_expo(self):
63
enricher = ReactNativeEnricher(_mock_store())
64
assert "expo" in enricher.detection_patterns
65
66
def test_returns_list(self):
67
enricher = ReactNativeEnricher(_mock_store())
68
assert isinstance(enricher.detection_patterns, list)
69
70
def test_has_at_least_two_patterns(self):
71
enricher = ReactNativeEnricher(_mock_store())
72
assert len(enricher.detection_patterns) >= 2
73
74
75
# ── detect() ─────────────────────────────────────────────────────────────────
76
77
78
class TestDetect:
79
def test_returns_true_when_pattern_found(self):
80
store = _mock_store(result_set=[[1]])
81
enricher = ReactNativeEnricher(store)
82
assert enricher.detect() is True
83
84
def test_returns_false_when_no_match(self):
85
store = _mock_store(result_set=[[0]])
86
enricher = ReactNativeEnricher(store)
87
assert enricher.detect() is False
88
89
def test_returns_false_on_empty_result_set(self):
90
store = _mock_store(result_set=[])
91
enricher = ReactNativeEnricher(store)
92
assert enricher.detect() is False
93
94
def test_short_circuits_on_first_match(self):
95
store = _mock_store(result_set=[[1]])
96
enricher = ReactNativeEnricher(store)
97
assert enricher.detect() is True
98
assert store._graph.query.call_count == 1
99
100
101
# ── enrich() ─────────────────────────────────────────────────────────────────
102
103
104
class TestEnrich:
105
def _empty_enricher(self):
106
# 4 pattern queries: components, screens, hooks, navigation
107
store = _mock_store_with_responses([[], [], [], []])
108
return ReactNativeEnricher(store)
109
110
def test_returns_enrichment_result(self):
111
assert isinstance(self._empty_enricher().enrich(), EnrichmentResult)
112
113
def test_zero_promoted_when_no_matches(self):
114
result = self._empty_enricher().enrich()
115
assert result.promoted == 0
116
117
def test_patterns_found_keys_present(self):
118
result = self._empty_enricher().enrich()
119
assert "components" in result.patterns_found
120
assert "screens" in result.patterns_found
121
assert "hooks" in result.patterns_found
122
assert "navigation" in result.patterns_found
123
124
def test_promotes_component_nodes(self):
125
"""JSX/TSX nodes should become RNComponent."""
126
store = _mock_store_with_responses(
127
[
128
[["Button", "src/components/Button.tsx"]], # components
129
[], # screens
130
[], # hooks
131
[], # navigation
132
]
133
)
134
enricher = ReactNativeEnricher(store)
135
result = enricher.enrich()
136
assert result.promoted == 1
137
assert result.patterns_found["components"] == 1
138
139
calls = store._graph.query.call_args_list
140
promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
141
assert len(promote_calls) == 1
142
assert promote_calls[0][0][1]["semantic_type"] == "RNComponent"
143
144
def test_promotes_screen_nodes_by_path(self):
145
"""Nodes under /screens/ should become RNScreen."""
146
store = _mock_store_with_responses(
147
[
148
[], # components
149
[["HomeScreen", "src/screens/HomeScreen.tsx"]], # screens
150
[], # hooks
151
[], # navigation
152
]
153
)
154
enricher = ReactNativeEnricher(store)
155
result = enricher.enrich()
156
assert result.promoted == 1
157
assert result.patterns_found["screens"] == 1
158
159
calls = store._graph.query.call_args_list
160
promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
161
assert promote_calls[0][0][1]["semantic_type"] == "RNScreen"
162
163
def test_promotes_screen_nodes_by_name_suffix(self):
164
"""Nodes whose name ends with 'Screen' should become RNScreen."""
165
store = _mock_store_with_responses(
166
[
167
[], # components
168
[["ProfileScreen", "src/ProfileScreen.tsx"]], # screens
169
[], # hooks
170
[], # navigation
171
]
172
)
173
enricher = ReactNativeEnricher(store)
174
result = enricher.enrich()
175
assert result.promoted == 1
176
177
calls = store._graph.query.call_args_list
178
promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
179
assert promote_calls[0][0][1]["semantic_type"] == "RNScreen"
180
181
def test_promotes_hook_nodes(self):
182
"""use* functions should become RNHook."""
183
store = _mock_store_with_responses(
184
[
185
[], # components
186
[], # screens
187
[["useTheme", "src/hooks/useTheme.ts"]], # hooks
188
[], # navigation
189
]
190
)
191
enricher = ReactNativeEnricher(store)
192
result = enricher.enrich()
193
assert result.promoted == 1
194
assert result.patterns_found["hooks"] == 1
195
196
calls = store._graph.query.call_args_list
197
promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
198
assert promote_calls[0][0][1]["semantic_type"] == "RNHook"
199
200
def test_promotes_navigation_nodes(self):
201
"""createStackNavigator etc. should become RNNavigation."""
202
store = _mock_store_with_responses(
203
[
204
[], # components
205
[], # screens
206
[], # hooks
207
[["createStackNavigator", "src/navigation/AppNavigator.ts"]], # navigation
208
]
209
)
210
enricher = ReactNativeEnricher(store)
211
result = enricher.enrich()
212
assert result.promoted == 1
213
assert result.patterns_found["navigation"] == 1
214
215
calls = store._graph.query.call_args_list
216
promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
217
assert promote_calls[0][0][1]["semantic_type"] == "RNNavigation"
218
219
def test_promoted_accumulates_across_patterns(self):
220
# Each matched node triggers a _promote_node SET query after its pattern query.
221
store = _mock_store_with_responses(
222
[
223
[["View", "a.tsx"], ["Text", "b.tsx"]], # components query (2 rows)
224
None, # _promote_node for View
225
None, # _promote_node for Text
226
[["HomeScreen", "screens/Home.tsx"]], # screens query (1 row)
227
None, # _promote_node for HomeScreen
228
[["useAuth", "hooks/useAuth.ts"]], # hooks query (1 row)
229
None, # _promote_node for useAuth
230
[["NavigationContainer", "nav/App.ts"],
231
["createStackNavigator", "nav/App.ts"]], # navigation query (2 rows)
232
None, # _promote_node for NavigationContainer
233
None, # _promote_node for createStackNavigator
234
]
235
)
236
enricher = ReactNativeEnricher(store)
237
result = enricher.enrich()
238
assert result.promoted == 6
239
assert result.patterns_found["components"] == 2
240
assert result.patterns_found["screens"] == 1
241
assert result.patterns_found["hooks"] == 1
242
assert result.patterns_found["navigation"] == 2
243
244
def test_promote_node_called_with_correct_semantic_type_for_navigation(self):
245
store = _mock_store_with_responses(
246
[
247
[],
248
[],
249
[],
250
[["createBottomTabNavigator", "src/nav/Tabs.ts"]],
251
]
252
)
253
enricher = ReactNativeEnricher(store)
254
enricher.enrich()
255
256
calls = store._graph.query.call_args_list
257
promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
258
params = promote_calls[0][0][1]
259
assert params["name"] == "createBottomTabNavigator"
260
assert params["file_path"] == "src/nav/Tabs.ts"
261
assert params["semantic_type"] == "RNNavigation"
262

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button