migration: establish exact preserved app baseline
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
STATIC = ROOT / "static"
|
||||
|
||||
|
||||
class FrontendBoundaryTests(unittest.TestCase):
|
||||
def test_shared_api_is_the_only_application_fetch_exit(self) -> None:
|
||||
fetch_files = []
|
||||
for path in STATIC.rglob("*.js"):
|
||||
if "vendor" in path.parts:
|
||||
continue
|
||||
if re.search(r"\bfetch\s*\(", path.read_text(encoding="utf-8")):
|
||||
fetch_files.append(path.relative_to(STATIC).as_posix())
|
||||
self.assertEqual(fetch_files, ["shared/api.js"])
|
||||
|
||||
def test_shared_dependencies_load_before_application(self) -> None:
|
||||
html = (STATIC / "index.html").read_text(encoding="utf-8")
|
||||
ui_position = html.index('/ui-core.js')
|
||||
components_position = html.index('/shared/components.js')
|
||||
pages_position = html.index('/pages.config.js')
|
||||
runtime_position = html.index('/pages/runtime.js')
|
||||
state_position = html.index('/shared/state.js')
|
||||
api_position = html.index('/shared/api.js')
|
||||
shell_position = html.index('/shared/shell.js')
|
||||
app_position = html.index('/app.js')
|
||||
self.assertLess(ui_position, components_position)
|
||||
self.assertLess(components_position, pages_position)
|
||||
self.assertLess(pages_position, state_position)
|
||||
self.assertLess(pages_position, runtime_position)
|
||||
self.assertLess(runtime_position, state_position)
|
||||
self.assertLess(state_position, api_position)
|
||||
self.assertLess(api_position, shell_position)
|
||||
self.assertLess(shell_position, app_position)
|
||||
|
||||
def test_application_state_is_created_through_shared_boundary(self) -> None:
|
||||
app = (STATIC / "app.js").read_text(encoding="utf-8")
|
||||
self.assertIn("const state = window.XiaobaiState.create({", app)
|
||||
self.assertNotIn("const state = {", app)
|
||||
|
||||
def test_runtime_page_registry_matches_governance_registry(self) -> None:
|
||||
expected = json.loads(
|
||||
(ROOT / "config" / "pages.config.json").read_text(encoding="utf-8")
|
||||
)["pages"]
|
||||
runtime = (STATIC / "pages.config.js").read_text(encoding="utf-8")
|
||||
rows = re.findall(
|
||||
r'^\s*\["([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)", (true|false)\],$',
|
||||
runtime,
|
||||
re.MULTILINE,
|
||||
)
|
||||
actual = [
|
||||
{
|
||||
"id": row[0],
|
||||
"title": row[1],
|
||||
"feature": row[2],
|
||||
"group": row[3],
|
||||
"access": row[4],
|
||||
"default": row[5] == "true",
|
||||
"desktop_scroll": "page",
|
||||
"mobile_layout": "dedicated",
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_shell_owns_navigation_and_page_mounting(self) -> None:
|
||||
app = (STATIC / "app.js").read_text(encoding="utf-8")
|
||||
shell = (STATIC / "shared" / "shell.js").read_text(encoding="utf-8")
|
||||
self.assertNotIn("function syncNavigationState", app)
|
||||
self.assertNotIn("function initializeApplicationShell", app)
|
||||
self.assertIn("function syncNavigation(viewId)", shell)
|
||||
self.assertIn("function mount(viewId, mountOptions = {})", shell)
|
||||
self.assertIn("function openModalDialog(dialog)", shell)
|
||||
self.assertNotIn('document.querySelectorAll(".module-tab").forEach', app)
|
||||
|
||||
def test_every_registered_view_has_one_feature_page_module(self) -> None:
|
||||
html = (STATIC / "index.html").read_text(encoding="utf-8")
|
||||
runtime_position = html.index('/pages/runtime.js')
|
||||
app_position = html.index('/app.js')
|
||||
expected = {
|
||||
page["id"]: page["feature"]
|
||||
for page in json.loads(
|
||||
(ROOT / "config" / "pages.config.json").read_text(encoding="utf-8")
|
||||
)["pages"]
|
||||
}
|
||||
expected["screenerTrackingView"] = "screener"
|
||||
actual: dict[str, str] = {}
|
||||
for path in (STATIC / "pages").glob("*/page.js"):
|
||||
script_url = f'/pages/{path.parent.name}/page.js'
|
||||
self.assertIn(script_url, html)
|
||||
self.assertLess(runtime_position, html.index(script_url))
|
||||
self.assertLess(html.index(script_url), app_position)
|
||||
script = path.read_text(encoding="utf-8")
|
||||
for match in re.finditer(
|
||||
r'XiaobaiPageModules\.register\("([^"]+)",\s*\[(.*?)\]',
|
||||
script,
|
||||
re.DOTALL,
|
||||
):
|
||||
feature = match.group(1)
|
||||
for view_id in re.findall(r'"([A-Za-z][A-Za-z0-9]+)"', match.group(2)):
|
||||
self.assertNotIn(view_id, actual)
|
||||
actual[view_id] = feature
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_page_lifecycle_is_owned_outside_application_monolith(self) -> None:
|
||||
app = (STATIC / "app.js").read_text(encoding="utf-8")
|
||||
runtime = (STATIC / "pages" / "runtime.js").read_text(encoding="utf-8")
|
||||
start = app.index("function openView(")
|
||||
end = app.index("\nfunction initializeAutoTableSorting", start)
|
||||
open_view = app[start:end]
|
||||
self.assertIn("pageModules.beforeMount(viewId, previousView);", open_view)
|
||||
self.assertIn("pageModules.afterMount(viewId, previousView);", open_view)
|
||||
self.assertNotRegex(open_view, r'viewId\s*[!=]==?\s*"')
|
||||
self.assertIn("function beforeMount(viewId, previousView)", runtime)
|
||||
self.assertIn("function afterMount(viewId, previousView)", runtime)
|
||||
|
||||
def test_shared_empty_state_component_is_used_by_multiple_features(self) -> None:
|
||||
components = (STATIC / "shared" / "components.js").read_text(encoding="utf-8")
|
||||
app = (STATIC / "app.js").read_text(encoding="utf-8")
|
||||
self.assertIn("function emptyStateHtml(message, options = {})", components)
|
||||
self.assertIn("function renderEmptyState(target, message, options = {})", components)
|
||||
self.assertGreaterEqual(app.count("renderEmptyState("), 8)
|
||||
self.assertGreaterEqual(app.count("emptyStateHtml("), 8)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user