-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvalidate_frontend.py
More file actions
184 lines (145 loc) · 5.11 KB
/
Copy pathvalidate_frontend.py
File metadata and controls
184 lines (145 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"""Validate the frontend project: file structure, package.json, and build.
Run with: uv run python scripts/validate_frontend.py
Run with build: uv run python scripts/validate_frontend.py --build
"""
from __future__ import annotations
import json
import shutil
import subprocess
import sys
from pathlib import Path
FRONTEND_DIR = (
Path(__file__).resolve().parent.parent
/ "src"
/ "uipath"
/ "dev"
/ "server"
/ "frontend"
)
STATIC_DIR = FRONTEND_DIR.parent / "static"
def _check(label: str, fn):
"""Run a check function and report pass/fail."""
try:
fn()
print(f" PASS {label}")
return True
except Exception as e:
print(f" FAIL {label}: {e}")
return False
def check_frontend_dir_exists():
"""Frontend source directory exists."""
assert FRONTEND_DIR.exists(), f"Missing: {FRONTEND_DIR}"
def check_package_json():
"""package.json is valid and has required deps."""
pkg_file = FRONTEND_DIR / "package.json"
assert pkg_file.exists(), f"Missing: {pkg_file}"
pkg = json.loads(pkg_file.read_text())
assert "scripts" in pkg
assert "build" in pkg["scripts"]
assert "dev" in pkg["scripts"]
deps = pkg.get("dependencies", {})
assert "react" in deps, "Missing react dependency"
assert "reactflow" in deps, "Missing reactflow dependency"
assert "zustand" in deps, "Missing zustand dependency"
assert "dagre" in deps, "Missing dagre dependency"
def check_vite_config():
"""vite.config.ts exists and has proxy config."""
vite_cfg = FRONTEND_DIR / "vite.config.ts"
assert vite_cfg.exists(), f"Missing: {vite_cfg}"
content = vite_cfg.read_text()
assert "/api" in content, "Missing API proxy config"
assert "/ws" in content, "Missing WebSocket proxy config"
def check_tsconfig():
"""tsconfig.json exists."""
assert (FRONTEND_DIR / "tsconfig.json").exists()
def check_index_html():
"""index.html exists with root div."""
index = FRONTEND_DIR / "index.html"
assert index.exists(), f"Missing: {index}"
content = index.read_text()
assert 'id="root"' in content, "Missing root div"
def check_source_structure():
"""Key source files exist."""
src = FRONTEND_DIR / "src"
assert src.exists(), f"Missing: {src}"
required_files = [
"main.tsx",
"App.tsx",
"api/client.ts",
"api/websocket.ts",
"store/useRunStore.ts",
"store/useWebSocket.ts",
"components/graph/GraphPanel.tsx",
"components/traces/TraceTree.tsx",
"components/chat/ChatPanel.tsx",
"components/logs/LogPanel.tsx",
]
missing = [f for f in required_files if not (src / f).exists()]
assert not missing, f"Missing source files: {missing}"
def check_npm_available():
"""Check that npm is available on PATH."""
npm = "npm.cmd" if sys.platform == "win32" else "npm"
assert shutil.which(npm), f"{npm} not found on PATH"
def check_npm_install():
"""Verify npm install succeeds."""
npm = "npm.cmd" if sys.platform == "win32" else "npm"
result = subprocess.run(
[npm, "install"],
cwd=str(FRONTEND_DIR),
capture_output=True,
text=True,
)
assert result.returncode == 0, (
f"npm install failed (exit {result.returncode}):\n"
f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
)
def check_npm_build():
"""Verify npm run build succeeds and produces static files."""
npm = "npm.cmd" if sys.platform == "win32" else "npm"
result = subprocess.run(
[npm, "run", "build"],
cwd=str(FRONTEND_DIR),
capture_output=True,
text=True,
)
assert result.returncode == 0, (
f"npm run build failed (exit {result.returncode}):\n"
f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
)
assert STATIC_DIR.exists(), f"Build did not produce {STATIC_DIR}"
assert (STATIC_DIR / "index.html").exists(), "Build did not produce index.html"
def main():
"""Run frontend validation checks."""
do_build = "--build" in sys.argv
print("=" * 60)
print(
"Frontend Validation" + (" (with build)" if do_build else " (structure only)")
)
print("=" * 60)
checks = [
("Frontend directory exists", check_frontend_dir_exists),
("package.json valid with required deps", check_package_json),
("vite.config.ts with proxy config", check_vite_config),
("tsconfig.json exists", check_tsconfig),
("index.html with root div", check_index_html),
("Source file structure complete", check_source_structure),
]
if do_build:
checks.extend(
[
("npm available on PATH", check_npm_available),
("npm install succeeds", check_npm_install),
("npm run build succeeds", check_npm_build),
]
)
results = [_check(label, fn) for label, fn in checks]
print("=" * 60)
passed = sum(results)
total = len(results)
print(f"Results: {passed}/{total} passed")
if passed < total:
sys.exit(1)
else:
print("All checks passed!")
if __name__ == "__main__":
main()