"""V8.11: specialized tools share a single task-oriented UI without changing APIs.""" from app.version import VERSION from test_factory import client PROFESSIONAL = { '/editor/{site}/{entry}': 'editor', '/theme-gallery': 'theme', '/factory-studio': 'factory', '/operations': 'operations', '/production-v8': 'production', } def create_site(client): response = client.post('/api/sites', json={'name': 'V8.11 UI', 'theme': 'corporate'}) assert response.status_code == 201, response.text sid = response.json()['id'] entry = client.get(f'/api/sites/{sid}/entries').json()[0] return sid, entry['id'] def test_all_professional_pages_load_unified_shell(client): site, entry = create_site(client) for pattern, page_type in PROFESSIONAL.items(): uri = pattern.format(site=site, entry=entry) response = client.get(uri) assert response.status_code == 200, uri assert f'data-sf-page="{page_type}"' in response.text, uri assert '/ui/professional-ui.js' in response.text assert '/ui/professional-ui.css' in response.text def test_shared_navigation_assets_are_real_routes(client): css = client.get('/ui/professional-ui.css') js = client.get('/ui/professional-ui.js') assert css.status_code == 200 and js.status_code == 200 assert 'sf-bar-nav' in css.text and '.sf-editor-mobile-tabs' in css.text for code in ('siteforge:selected-site', 'sfGlobalSite', 'aria-current', 'MutationObserver'): assert code in js.text def test_site_switcher_avoids_cross_site_editor_paths(client): js = client.get('/ui/professional-ui.js').text assert "ctx==='editor'" in js assert "location.assign(route('design'))" in js assert 'items.some(s=>s.id===wanted)' in js # selected ID validated against visible sites assert 'siteforge:selected-site' in client.get('/ui/workspace.js').text def test_professional_forms_keep_validation_and_csrf(client): js = client.get('/ui/professional-ui.js').text assert "addEventListener('invalid'" in js assert "aria-invalid" in js assert 'X-SiteForge-CSRF' in client.get('/ui/operations.js').text assert 'X-SiteForge-CSRF' in client.get('/ui/workspace.js').text def test_operations_are_split_into_three_steps(client): shell = client.get('/ui/professional-ui.js').text assert '② 生成静态网站' in shell assert '③ 审批与发布' in shell assert "role','tabpanel'" in shell page = client.get('/operations').text assert all(word in page for word in ('CMS 数据迁移', '生产构建任务', '远程发布')) def test_editor_preserves_draft_publish_and_mobile_panels(client): site, entry = create_site(client) html = client.get(f'/editor/{site}/{entry}').text assert 'Studio.save()' in html and 'Studio.publishDesign()' in html js = client.get('/ui/professional-ui.js').text assert all(word in js for word in ('结构', '页面预览', '属性设置', "dataset.mobilePane")) assert 'sf-editor-actiongroup' in js def test_health_and_main_workspace_match_unified_release(client): assert client.get('/api/health').json()['version'] == VERSION assert 'const headings=' in client.get('/ui/workspace.js').text assert 'new URLSearchParams(location.search)' in client.get('/ui/workspace.js').text def test_existing_theme_and_content_apis_unchanged(client): sid, _ = create_site(client) assert client.get('/api/themes').status_code == 200 assert client.get(f'/api/sites/{sid}/entries').status_code == 200 assert client.get(f'/api/sites/{sid}/builds').status_code == 200