python2mobile 1.0.1__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.
- examples/example_ecommerce_app.py +189 -0
- examples/example_todo_app.py +159 -0
- p2m/__init__.py +31 -0
- p2m/cli.py +470 -0
- p2m/config.py +205 -0
- p2m/core/__init__.py +18 -0
- p2m/core/api.py +191 -0
- p2m/core/ast_walker.py +171 -0
- p2m/core/database.py +192 -0
- p2m/core/events.py +56 -0
- p2m/core/render_engine.py +597 -0
- p2m/core/runtime.py +128 -0
- p2m/core/state.py +51 -0
- p2m/core/validator.py +284 -0
- p2m/devserver/__init__.py +9 -0
- p2m/devserver/server.py +84 -0
- p2m/i18n/__init__.py +7 -0
- p2m/i18n/translator.py +74 -0
- p2m/imagine/__init__.py +35 -0
- p2m/imagine/agent.py +463 -0
- p2m/imagine/legacy.py +217 -0
- p2m/llm/__init__.py +20 -0
- p2m/llm/anthropic_provider.py +78 -0
- p2m/llm/base.py +42 -0
- p2m/llm/compatible_provider.py +120 -0
- p2m/llm/factory.py +72 -0
- p2m/llm/ollama_provider.py +89 -0
- p2m/llm/openai_provider.py +79 -0
- p2m/testing/__init__.py +41 -0
- p2m/ui/__init__.py +43 -0
- p2m/ui/components.py +301 -0
- python2mobile-1.0.1.dist-info/METADATA +238 -0
- python2mobile-1.0.1.dist-info/RECORD +50 -0
- python2mobile-1.0.1.dist-info/WHEEL +5 -0
- python2mobile-1.0.1.dist-info/entry_points.txt +2 -0
- python2mobile-1.0.1.dist-info/top_level.txt +3 -0
- tests/test_basic_engine.py +281 -0
- tests/test_build_generation.py +603 -0
- tests/test_build_test_gate.py +150 -0
- tests/test_carousel_modal.py +84 -0
- tests/test_config_system.py +272 -0
- tests/test_i18n.py +101 -0
- tests/test_ifood_app_integration.py +172 -0
- tests/test_imagine_cli.py +133 -0
- tests/test_imagine_command.py +341 -0
- tests/test_llm_providers.py +321 -0
- tests/test_new_apps_integration.py +588 -0
- tests/test_ollama_functional.py +329 -0
- tests/test_real_world_apps.py +228 -0
- tests/test_run_integration.py +776 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Functional tests for P2M Imagine with Ollama (qwen3-coder:latest)
|
|
3
|
+
Tests against https://ollama.dataseed.com.br/
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Add project to path
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
11
|
+
|
|
12
|
+
from p2m.imagine.legacy import CodeImaginer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Ollama configuration
|
|
16
|
+
OLLAMA_BASE_URL = "https://ollama.dataseed.com.br"
|
|
17
|
+
OLLAMA_MODEL = "qwen3-coder:latest"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_ollama_connection():
|
|
21
|
+
"""Test connection to Ollama server"""
|
|
22
|
+
print("\n🧪 Test 1: Ollama Server Connection")
|
|
23
|
+
|
|
24
|
+
imaginer = CodeImaginer(
|
|
25
|
+
provider="ollama",
|
|
26
|
+
model=OLLAMA_MODEL,
|
|
27
|
+
base_url=OLLAMA_BASE_URL
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
valid, msg = imaginer.validate_config()
|
|
31
|
+
assert valid, f"Configuration should be valid: {msg}"
|
|
32
|
+
|
|
33
|
+
success, msg = imaginer.create_provider()
|
|
34
|
+
if not success:
|
|
35
|
+
print(f"⚠️ Cannot connect to Ollama: {msg}")
|
|
36
|
+
print(f" Server: {OLLAMA_BASE_URL}")
|
|
37
|
+
print(f" Model: {OLLAMA_MODEL}")
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
print(f"✅ Connected to Ollama server")
|
|
41
|
+
print(f" Server: {OLLAMA_BASE_URL}")
|
|
42
|
+
print(f" Model: {OLLAMA_MODEL}")
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_hello_world_app():
|
|
47
|
+
"""Test generating a simple Hello World app"""
|
|
48
|
+
print("\n🧪 Test 2: Generate Hello World App")
|
|
49
|
+
|
|
50
|
+
imaginer = CodeImaginer(
|
|
51
|
+
provider="ollama",
|
|
52
|
+
model=OLLAMA_MODEL,
|
|
53
|
+
base_url=OLLAMA_BASE_URL
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
valid, msg = imaginer.validate_config()
|
|
57
|
+
if not valid:
|
|
58
|
+
print(f"⚠️ Configuration invalid: {msg}")
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
success, msg = imaginer.create_provider()
|
|
62
|
+
if not success:
|
|
63
|
+
print(f"⚠️ Cannot create provider: {msg}")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
description = "Create a simple Hello World app with a centered text that says 'Hello World' and a button that says 'Click Me'"
|
|
67
|
+
success, code = imaginer.generate_code(description)
|
|
68
|
+
|
|
69
|
+
if not success:
|
|
70
|
+
print(f"❌ Code generation failed: {code}")
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
print(f"✅ Code generated successfully")
|
|
74
|
+
print(f" Lines of code: {len(code.split(chr(10)))}")
|
|
75
|
+
|
|
76
|
+
# Validate code
|
|
77
|
+
valid, msg = imaginer.validate_code(code)
|
|
78
|
+
if not valid:
|
|
79
|
+
print(f"⚠️ Code validation failed: {msg}")
|
|
80
|
+
print(f"\nGenerated code:\n{code[:500]}...")
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
print(f"✅ Code validation passed")
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_todo_app():
|
|
88
|
+
"""Test generating a Todo app"""
|
|
89
|
+
print("\n🧪 Test 3: Generate Todo App")
|
|
90
|
+
|
|
91
|
+
imaginer = CodeImaginer(
|
|
92
|
+
provider="ollama",
|
|
93
|
+
model=OLLAMA_MODEL,
|
|
94
|
+
base_url=OLLAMA_BASE_URL
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
valid, msg = imaginer.validate_config()
|
|
98
|
+
if not valid:
|
|
99
|
+
print(f"⚠️ Configuration invalid: {msg}")
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
success, msg = imaginer.create_provider()
|
|
103
|
+
if not success:
|
|
104
|
+
print(f"⚠️ Cannot create provider: {msg}")
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
description = """Create a Todo app with:
|
|
108
|
+
- A title 'My Todo List'
|
|
109
|
+
- An input field to add new todos
|
|
110
|
+
- A button to add todos
|
|
111
|
+
- A list of todos with delete buttons
|
|
112
|
+
- Show count of completed todos"""
|
|
113
|
+
|
|
114
|
+
success, code = imaginer.generate_code(description)
|
|
115
|
+
|
|
116
|
+
if not success:
|
|
117
|
+
print(f"❌ Code generation failed: {code}")
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
print(f"✅ Code generated successfully")
|
|
121
|
+
print(f" Lines of code: {len(code.split(chr(10)))}")
|
|
122
|
+
|
|
123
|
+
# Validate code
|
|
124
|
+
valid, msg = imaginer.validate_code(code)
|
|
125
|
+
if not valid:
|
|
126
|
+
print(f"⚠️ Code validation failed: {msg}")
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
print(f"✅ Code validation passed")
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_counter_app():
|
|
134
|
+
"""Test generating a Counter app"""
|
|
135
|
+
print("\n🧪 Test 4: Generate Counter App")
|
|
136
|
+
|
|
137
|
+
imaginer = CodeImaginer(
|
|
138
|
+
provider="ollama",
|
|
139
|
+
model=OLLAMA_MODEL,
|
|
140
|
+
base_url=OLLAMA_BASE_URL
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
valid, msg = imaginer.validate_config()
|
|
144
|
+
if not valid:
|
|
145
|
+
print(f"⚠️ Configuration invalid: {msg}")
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
success, msg = imaginer.create_provider()
|
|
149
|
+
if not success:
|
|
150
|
+
print(f"⚠️ Cannot create provider: {msg}")
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
description = """Create a Counter app with:
|
|
154
|
+
- A large display showing the current count
|
|
155
|
+
- An Increment button (green)
|
|
156
|
+
- A Decrement button (red)
|
|
157
|
+
- A Reset button (gray)
|
|
158
|
+
- Start count at 0"""
|
|
159
|
+
|
|
160
|
+
success, code = imaginer.generate_code(description)
|
|
161
|
+
|
|
162
|
+
if not success:
|
|
163
|
+
print(f"❌ Code generation failed: {code}")
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
print(f"✅ Code generated successfully")
|
|
167
|
+
print(f" Lines of code: {len(code.split(chr(10)))}")
|
|
168
|
+
|
|
169
|
+
# Validate code
|
|
170
|
+
valid, msg = imaginer.validate_code(code)
|
|
171
|
+
if not valid:
|
|
172
|
+
print(f"⚠️ Code validation failed: {msg}")
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
print(f"✅ Code validation passed")
|
|
176
|
+
return True
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_form_app():
|
|
180
|
+
"""Test generating a Form app"""
|
|
181
|
+
print("\n🧪 Test 5: Generate Form App")
|
|
182
|
+
|
|
183
|
+
imaginer = CodeImaginer(
|
|
184
|
+
provider="ollama",
|
|
185
|
+
model=OLLAMA_MODEL,
|
|
186
|
+
base_url=OLLAMA_BASE_URL
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
valid, msg = imaginer.validate_config()
|
|
190
|
+
if not valid:
|
|
191
|
+
print(f"⚠️ Configuration invalid: {msg}")
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
success, msg = imaginer.create_provider()
|
|
195
|
+
if not success:
|
|
196
|
+
print(f"⚠️ Cannot create provider: {msg}")
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
description = """Create a Contact Form app with:
|
|
200
|
+
- A title 'Contact Us'
|
|
201
|
+
- Input field for name
|
|
202
|
+
- Input field for email
|
|
203
|
+
- Input field for message
|
|
204
|
+
- A submit button
|
|
205
|
+
- Display a success message when submitted"""
|
|
206
|
+
|
|
207
|
+
success, code = imaginer.generate_code(description)
|
|
208
|
+
|
|
209
|
+
if not success:
|
|
210
|
+
print(f"❌ Code generation failed: {code}")
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
print(f"✅ Code generated successfully")
|
|
214
|
+
print(f" Lines of code: {len(code.split(chr(10)))}")
|
|
215
|
+
|
|
216
|
+
# Validate code
|
|
217
|
+
valid, msg = imaginer.validate_code(code)
|
|
218
|
+
if not valid:
|
|
219
|
+
print(f"⚠️ Code validation failed: {msg}")
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
print(f"✅ Code validation passed")
|
|
223
|
+
return True
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_dashboard_app():
|
|
227
|
+
"""Test generating a Dashboard app"""
|
|
228
|
+
print("\n🧪 Test 6: Generate Dashboard App")
|
|
229
|
+
|
|
230
|
+
imaginer = CodeImaginer(
|
|
231
|
+
provider="ollama",
|
|
232
|
+
model=OLLAMA_MODEL,
|
|
233
|
+
base_url=OLLAMA_BASE_URL
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
valid, msg = imaginer.validate_config()
|
|
237
|
+
if not valid:
|
|
238
|
+
print(f"⚠️ Configuration invalid: {msg}")
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
success, msg = imaginer.create_provider()
|
|
242
|
+
if not success:
|
|
243
|
+
print(f"⚠️ Cannot create provider: {msg}")
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
description = """Create a Dashboard app with:
|
|
247
|
+
- A header with title 'Dashboard'
|
|
248
|
+
- Three cards showing:
|
|
249
|
+
- Users: 1,234
|
|
250
|
+
- Orders: 567
|
|
251
|
+
- Revenue: $89,012
|
|
252
|
+
- A refresh button
|
|
253
|
+
- Use different colors for each card"""
|
|
254
|
+
|
|
255
|
+
success, code = imaginer.generate_code(description)
|
|
256
|
+
|
|
257
|
+
if not success:
|
|
258
|
+
print(f"❌ Code generation failed: {code}")
|
|
259
|
+
return False
|
|
260
|
+
|
|
261
|
+
print(f"✅ Code generated successfully")
|
|
262
|
+
print(f" Lines of code: {len(code.split(chr(10)))}")
|
|
263
|
+
|
|
264
|
+
# Validate code
|
|
265
|
+
valid, msg = imaginer.validate_code(code)
|
|
266
|
+
if not valid:
|
|
267
|
+
print(f"⚠️ Code validation failed: {msg}")
|
|
268
|
+
return False
|
|
269
|
+
|
|
270
|
+
print(f"✅ Code validation passed")
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def run_all_tests():
|
|
275
|
+
"""Run all functional tests"""
|
|
276
|
+
print("\n" + "="*60)
|
|
277
|
+
print("🧪 P2M Imagine - Ollama Functional Tests")
|
|
278
|
+
print("="*60)
|
|
279
|
+
print(f"Server: {OLLAMA_BASE_URL}")
|
|
280
|
+
print(f"Model: {OLLAMA_MODEL}")
|
|
281
|
+
|
|
282
|
+
tests = [
|
|
283
|
+
test_ollama_connection,
|
|
284
|
+
test_hello_world_app,
|
|
285
|
+
test_todo_app,
|
|
286
|
+
test_counter_app,
|
|
287
|
+
test_form_app,
|
|
288
|
+
test_dashboard_app,
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
results = []
|
|
292
|
+
|
|
293
|
+
for test_func in tests:
|
|
294
|
+
try:
|
|
295
|
+
result = test_func()
|
|
296
|
+
results.append((test_func.__name__, result))
|
|
297
|
+
except Exception as e:
|
|
298
|
+
results.append((test_func.__name__, False))
|
|
299
|
+
print(f"❌ {test_func.__name__} failed: {e}")
|
|
300
|
+
import traceback
|
|
301
|
+
traceback.print_exc()
|
|
302
|
+
|
|
303
|
+
# Summary
|
|
304
|
+
print("\n" + "="*60)
|
|
305
|
+
print("📊 Test Summary")
|
|
306
|
+
print("="*60)
|
|
307
|
+
|
|
308
|
+
passed = sum(1 for _, success in results if success)
|
|
309
|
+
total = len(results)
|
|
310
|
+
|
|
311
|
+
for name, success in results:
|
|
312
|
+
status = "✅ PASS" if success else "❌ FAIL"
|
|
313
|
+
print(f"{status} - {name}")
|
|
314
|
+
|
|
315
|
+
print(f"\n📈 Results: {passed}/{total} tests passed")
|
|
316
|
+
|
|
317
|
+
if passed == total:
|
|
318
|
+
print("\n🎉 All tests passed!")
|
|
319
|
+
elif passed > 0:
|
|
320
|
+
print(f"\n⚠️ {total - passed} test(s) failed (connection issues?)")
|
|
321
|
+
else:
|
|
322
|
+
print(f"\n❌ All tests failed (cannot connect to Ollama server)")
|
|
323
|
+
|
|
324
|
+
return passed > 0 # Return True if at least one test passed
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
if __name__ == "__main__":
|
|
328
|
+
success = run_all_tests()
|
|
329
|
+
sys.exit(0 if success else 1)
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test suite for real-world P2M applications
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Add project to path
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
10
|
+
|
|
11
|
+
from p2m.core import Render, RenderEngine
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_todo_app():
|
|
15
|
+
"""Test todo app example"""
|
|
16
|
+
print("\n🧪 Test 1: Todo App Example")
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
# Import the example
|
|
20
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "examples"))
|
|
21
|
+
import example_todo_app
|
|
22
|
+
|
|
23
|
+
# Execute the view
|
|
24
|
+
component_tree = Render.execute(example_todo_app.create_view)
|
|
25
|
+
|
|
26
|
+
assert component_tree is not None
|
|
27
|
+
assert component_tree["type"] == "Container"
|
|
28
|
+
|
|
29
|
+
# Render to HTML
|
|
30
|
+
engine = RenderEngine()
|
|
31
|
+
html = engine.render(component_tree, mobile_frame=False)
|
|
32
|
+
|
|
33
|
+
assert html is not None
|
|
34
|
+
assert "My Todo App" in html
|
|
35
|
+
assert "Todo List" in html
|
|
36
|
+
|
|
37
|
+
print("✅ Todo app test passed")
|
|
38
|
+
print(f" Generated HTML: {len(html)} bytes")
|
|
39
|
+
return True
|
|
40
|
+
except Exception as e:
|
|
41
|
+
print(f"❌ Todo app test failed: {e}")
|
|
42
|
+
import traceback
|
|
43
|
+
traceback.print_exc()
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_ecommerce_app():
|
|
48
|
+
"""Test e-commerce app example"""
|
|
49
|
+
print("\n🧪 Test 2: E-commerce App Example")
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
# Import the example
|
|
53
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "examples"))
|
|
54
|
+
import example_ecommerce_app
|
|
55
|
+
|
|
56
|
+
# Execute the view
|
|
57
|
+
component_tree = Render.execute(example_ecommerce_app.create_view)
|
|
58
|
+
|
|
59
|
+
assert component_tree is not None
|
|
60
|
+
assert component_tree["type"] == "Container"
|
|
61
|
+
|
|
62
|
+
# Render to HTML
|
|
63
|
+
engine = RenderEngine()
|
|
64
|
+
html = engine.render(component_tree, mobile_frame=False)
|
|
65
|
+
|
|
66
|
+
assert html is not None
|
|
67
|
+
assert "TechStore" in html
|
|
68
|
+
assert "Featured Products" in html
|
|
69
|
+
|
|
70
|
+
print("✅ E-commerce app test passed")
|
|
71
|
+
print(f" Generated HTML: {len(html)} bytes")
|
|
72
|
+
return True
|
|
73
|
+
except Exception as e:
|
|
74
|
+
print(f"❌ E-commerce app test failed: {e}")
|
|
75
|
+
import traceback
|
|
76
|
+
traceback.print_exc()
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_todo_app_with_mobile_frame():
|
|
81
|
+
"""Test todo app with mobile frame"""
|
|
82
|
+
print("\n🧪 Test 3: Todo App with Mobile Frame")
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "examples"))
|
|
86
|
+
import example_todo_app
|
|
87
|
+
|
|
88
|
+
component_tree = Render.execute(example_todo_app.create_view)
|
|
89
|
+
|
|
90
|
+
# Render with mobile frame
|
|
91
|
+
engine = RenderEngine()
|
|
92
|
+
html = engine.render(component_tree, mobile_frame=True)
|
|
93
|
+
|
|
94
|
+
assert html is not None
|
|
95
|
+
assert "mobile-frame" in html
|
|
96
|
+
assert "375px" in html
|
|
97
|
+
assert "My Todo App" in html
|
|
98
|
+
|
|
99
|
+
print("✅ Todo app with mobile frame test passed")
|
|
100
|
+
print(f" Generated HTML: {len(html)} bytes")
|
|
101
|
+
return True
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print(f"❌ Todo app with mobile frame test failed: {e}")
|
|
104
|
+
import traceback
|
|
105
|
+
traceback.print_exc()
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_ecommerce_app_with_mobile_frame():
|
|
110
|
+
"""Test e-commerce app with mobile frame"""
|
|
111
|
+
print("\n🧪 Test 4: E-commerce App with Mobile Frame")
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "examples"))
|
|
115
|
+
import example_ecommerce_app
|
|
116
|
+
|
|
117
|
+
component_tree = Render.execute(example_ecommerce_app.create_view)
|
|
118
|
+
|
|
119
|
+
# Render with mobile frame
|
|
120
|
+
engine = RenderEngine()
|
|
121
|
+
html = engine.render(component_tree, mobile_frame=True)
|
|
122
|
+
|
|
123
|
+
assert html is not None
|
|
124
|
+
assert "mobile-frame" in html
|
|
125
|
+
assert "TechStore" in html
|
|
126
|
+
|
|
127
|
+
print("✅ E-commerce app with mobile frame test passed")
|
|
128
|
+
print(f" Generated HTML: {len(html)} bytes")
|
|
129
|
+
return True
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"❌ E-commerce app with mobile frame test failed: {e}")
|
|
132
|
+
import traceback
|
|
133
|
+
traceback.print_exc()
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_app_structure():
|
|
138
|
+
"""Test app component structure"""
|
|
139
|
+
print("\n🧪 Test 5: App Component Structure")
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "examples"))
|
|
143
|
+
import example_todo_app
|
|
144
|
+
|
|
145
|
+
component_tree = Render.execute(example_todo_app.create_view)
|
|
146
|
+
|
|
147
|
+
# Verify structure
|
|
148
|
+
assert component_tree["type"] == "Container"
|
|
149
|
+
assert len(component_tree["children"]) > 0
|
|
150
|
+
|
|
151
|
+
# Count different component types
|
|
152
|
+
def count_components(comp, comp_type):
|
|
153
|
+
count = 0
|
|
154
|
+
if comp.get("type") == comp_type:
|
|
155
|
+
count += 1
|
|
156
|
+
for child in comp.get("children", []):
|
|
157
|
+
if isinstance(child, dict):
|
|
158
|
+
count += count_components(child, comp_type)
|
|
159
|
+
return count
|
|
160
|
+
|
|
161
|
+
text_count = count_components(component_tree, "Text")
|
|
162
|
+
button_count = count_components(component_tree, "Button")
|
|
163
|
+
card_count = count_components(component_tree, "Card")
|
|
164
|
+
|
|
165
|
+
assert text_count > 0, "Should have Text components"
|
|
166
|
+
assert button_count > 0, "Should have Button components"
|
|
167
|
+
|
|
168
|
+
print(f"✅ App structure test passed")
|
|
169
|
+
print(f" Text components: {text_count}")
|
|
170
|
+
print(f" Button components: {button_count}")
|
|
171
|
+
print(f" Card components: {card_count}")
|
|
172
|
+
return True
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(f"❌ App structure test failed: {e}")
|
|
175
|
+
import traceback
|
|
176
|
+
traceback.print_exc()
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def run_all_tests():
|
|
181
|
+
"""Run all tests"""
|
|
182
|
+
print("\n" + "="*60)
|
|
183
|
+
print("🧪 P2M Real-World Apps Test Suite")
|
|
184
|
+
print("="*60)
|
|
185
|
+
|
|
186
|
+
tests = [
|
|
187
|
+
test_todo_app,
|
|
188
|
+
test_ecommerce_app,
|
|
189
|
+
test_todo_app_with_mobile_frame,
|
|
190
|
+
test_ecommerce_app_with_mobile_frame,
|
|
191
|
+
test_app_structure,
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
results = []
|
|
195
|
+
|
|
196
|
+
for test_func in tests:
|
|
197
|
+
try:
|
|
198
|
+
result = test_func()
|
|
199
|
+
results.append((test_func.__name__, result))
|
|
200
|
+
except Exception as e:
|
|
201
|
+
results.append((test_func.__name__, False))
|
|
202
|
+
print(f"❌ {test_func.__name__} failed: {e}")
|
|
203
|
+
|
|
204
|
+
# Summary
|
|
205
|
+
print("\n" + "="*60)
|
|
206
|
+
print("📊 Test Summary")
|
|
207
|
+
print("="*60)
|
|
208
|
+
|
|
209
|
+
passed = sum(1 for _, success in results if success)
|
|
210
|
+
total = len(results)
|
|
211
|
+
|
|
212
|
+
for name, success in results:
|
|
213
|
+
status = "✅ PASS" if success else "❌ FAIL"
|
|
214
|
+
print(f"{status} - {name}")
|
|
215
|
+
|
|
216
|
+
print(f"\n📈 Results: {passed}/{total} tests passed")
|
|
217
|
+
|
|
218
|
+
if passed == total:
|
|
219
|
+
print("\n🎉 All tests passed!")
|
|
220
|
+
else:
|
|
221
|
+
print(f"\n⚠️ {total - passed} test(s) failed")
|
|
222
|
+
|
|
223
|
+
return passed == total
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
success = run_all_tests()
|
|
228
|
+
sys.exit(0 if success else 1)
|