143 lines
6.6 KiB
Python
143 lines
6.6 KiB
Python
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def test_frontend_v2_shell_assets_exist_and_are_indexed():
|
|
assert (ROOT / "web/static/css/ewoooc-tokens.css").exists()
|
|
assert (ROOT / "web/static/css/ewoooc-shell.css").exists()
|
|
assert (ROOT / "templates/ewoooc_base.html").exists()
|
|
assert (ROOT / "templates/components/_ewoooc_shell.html").exists()
|
|
|
|
agents = (ROOT / "AGENTS.md").read_text(encoding="utf-8")
|
|
constitution = (ROOT / "CONSTITUTION.md").read_text(encoding="utf-8")
|
|
roadmap = (ROOT / "docs/guides/frontend_upgrade_roadmap.md").read_text(encoding="utf-8")
|
|
|
|
assert "docs/guides/frontend_upgrade_roadmap.md" in agents
|
|
assert "前端 V2 視覺基準" in constitution
|
|
assert "禁止用 mock data" in roadmap
|
|
|
|
|
|
def test_frontend_v2_shell_uses_real_runtime_context():
|
|
shell = (ROOT / "templates/components/_ewoooc_shell.html").read_text(encoding="utf-8")
|
|
base = (ROOT / "templates/ewoooc_base.html").read_text(encoding="utf-8")
|
|
|
|
assert "scheduler_stats" in shell
|
|
assert "session.get('username')" in shell
|
|
assert "next_run|default(None)" in shell
|
|
assert "components/_ewoooc_shell.html" in base
|
|
assert 'name="ui" value="v2"' not in base
|
|
|
|
forbidden_markers = [
|
|
"mockProducts",
|
|
"mockData",
|
|
"fake",
|
|
"假商品",
|
|
"假 KPI",
|
|
]
|
|
combined = shell + "\n" + base
|
|
assert all(marker not in combined for marker in forbidden_markers)
|
|
|
|
|
|
def test_dashboard_v2_is_production_default_and_uses_real_dashboard_data():
|
|
route_source = (ROOT / "routes/dashboard_routes.py").read_text(encoding="utf-8")
|
|
dashboard = (ROOT / "templates/dashboard_v2.html").read_text(encoding="utf-8")
|
|
|
|
assert "request.args.get('ui') == 'legacy'" in route_source
|
|
assert "template_name = 'dashboard.html' if request.args.get('ui') == 'legacy' else 'dashboard_v2.html'" in route_source
|
|
assert "get_full_dashboard_data()" in route_source
|
|
assert "MockRecord" not in route_source
|
|
assert "{% for item in items %}" in dashboard
|
|
assert "ui='v2'" not in dashboard
|
|
assert 'name="ui" value="v2"' not in dashboard
|
|
assert "mockProducts" not in dashboard
|
|
assert "假商品" not in dashboard
|
|
|
|
|
|
def test_dashboard_v2_restores_real_price_history_chart():
|
|
route_source = (ROOT / "routes/api_routes.py").read_text(encoding="utf-8")
|
|
dashboard = (ROOT / "templates/dashboard_v2.html").read_text(encoding="utf-8")
|
|
|
|
assert "@api_bp.route('/api/history/<int:product_id>')" in route_source
|
|
assert "session.query(PriceRecord)" in route_source
|
|
assert "PriceRecord.product_id == product_id" in route_source
|
|
assert "https://cdn.jsdelivr.net/npm/chart.js" in dashboard
|
|
assert 'id="historyModal"' in dashboard
|
|
assert 'id="priceChart"' in dashboard
|
|
assert "data-product-id=\"{{ product.id }}\"" in dashboard
|
|
assert "data-history-trigger" in dashboard
|
|
assert "onclick=\"event.stopPropagation(); showHistory(this.dataset.productId, this.dataset.productName);\"" in dashboard
|
|
assert "fetch(`/api/history/${productId}`)" in dashboard
|
|
assert "priceChartInstance = new Chart" in dashboard
|
|
assert "目前沒有可顯示的歷史價格紀錄" in dashboard
|
|
|
|
|
|
def test_edm_dashboard_v2_is_production_default_and_uses_real_campaign_data():
|
|
route_source = (ROOT / "routes/edm_routes.py").read_text(encoding="utf-8")
|
|
template = (ROOT / "templates/edm_dashboard_v2.html").read_text(encoding="utf-8")
|
|
|
|
assert route_source.count("request.args.get('ui') == 'legacy'") == 5
|
|
assert "template_name = 'edm_dashboard.html' if request.args.get('ui') == 'legacy' else 'edm_dashboard_v2.html'" in route_source
|
|
assert "{% for slot, stats in slot_stats.items() %}" in template
|
|
assert "{% for item in items %}" in template
|
|
assert "scheduler_stats.get(task_key, [])" in template
|
|
assert "?ui=v2" not in template
|
|
assert "ui='v2'" not in template
|
|
assert "mock" not in template.lower()
|
|
assert "假商品" not in template
|
|
|
|
|
|
def test_vendor_stockout_v2_is_production_default_and_uses_real_vendor_data():
|
|
route_source = (ROOT / "routes/vendor_routes.py").read_text(encoding="utf-8")
|
|
template = (ROOT / "templates/vendor_stockout_index_v2.html").read_text(encoding="utf-8")
|
|
|
|
assert "request.args.get('ui') == 'legacy'" in route_source
|
|
assert not (ROOT / "web/templates/vendor_stockout_index_v2.html").exists()
|
|
assert "vendor_stockout_index_v2.html" in route_source
|
|
assert "_get_vendor_dashboard_stats()" in route_source
|
|
assert "session.query(VendorStockout).count()" in route_source
|
|
assert "EmailSendLog" in route_source
|
|
assert "stats.pending_stockouts" in template
|
|
assert "stats.email_success_rate" in template
|
|
assert "ui='v2'" not in template
|
|
assert "mock" not in template.lower()
|
|
assert "假" not in template
|
|
|
|
|
|
def test_vendor_stockout_list_v2_is_feature_flagged_and_uses_real_stockout_rows():
|
|
route_source = (ROOT / "routes/vendor_routes.py").read_text(encoding="utf-8")
|
|
template = (ROOT / "templates/vendor_stockout_list_v2.html").read_text(encoding="utf-8")
|
|
|
|
assert "vendor_stockout_list_v2.html" in route_source
|
|
assert "_get_vendor_stockout_list_context()" in route_source
|
|
assert "_apply_stockout_filters(" in route_source
|
|
assert "session.query(VendorStockout)" in route_source
|
|
assert "VendorStockout.status == 'pending'" in route_source
|
|
assert "VendorStockout.batch_id == batch_id" in route_source
|
|
assert "{% for record in records %}" in template
|
|
assert "{% for batch in batches %}" in template
|
|
assert "stats.pending" in template
|
|
assert "record.product_code" in template
|
|
assert "record.product_name" in template
|
|
assert "record.vendor_name" in template
|
|
assert "ui='v2'" not in template
|
|
assert "mock" not in template.lower()
|
|
assert "假" not in template
|
|
|
|
|
|
def test_vendor_stockout_import_v2_is_feature_flagged_and_does_not_ship_sample_rows():
|
|
route_source = (ROOT / "routes/vendor_routes.py").read_text(encoding="utf-8")
|
|
template = (ROOT / "templates/vendor_stockout_import_v2.html").read_text(encoding="utf-8")
|
|
template_function = route_source.split("def api_import_template():", 1)[1].split("@vendor_bp.route('/api/stockout/list'", 1)[0]
|
|
|
|
assert "vendor_stockout_import_v2.html" in route_source
|
|
assert "template_columns = [" in template_function
|
|
assert "pd.DataFrame(columns=template_columns)" in template_function
|
|
assert "fetchWithCSRF('/vendor-stockout/api/import/excel'" in template
|
|
assert "vendor_stockout" in template
|
|
assert "範例" not in template_function
|
|
assert "ui='v2'" not in template
|
|
assert "mock" not in template.lower()
|
|
assert "假" not in template
|