feat: Phase 2 complete — 13 Phases of COBOL type classification and test benchmark
P0.6: gcov infrastructure P1: extract_structure output expansion (11 new feature fields) P2: Confusion group rule engine (8 pairs + contradiction + backtrack) P3: 4-factor confidence calculation + quality gate update P4: 33+2 COBOL program type test samples (22 files, 7 categories) P5: parametrized/ test data generation engine P6: japanese_data.py lookup tables P7-10: Type-specific test suites (~159 parametrized tests) P11: Full classification pipeline (classify_program) + orchestrator integration P12: Documentation (module-interfaces, test-plan v3.0, coverage-matrix) Architecture decisions: - classification_pipeline/ merged to hina/pipeline/ - parametrized/ as independent module - japanese_data.py as root-level file - hina/__all__ only exports classify_program() Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
Playwright E2E tests for COBOL-Java Migration Platform Web UI.
|
||||
Server must be running: python -m uvicorn web.api:app --host 127.0.0.1 --port 8000
|
||||
"""
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect, sync_playwright
|
||||
|
||||
BASE_URL = "http://127.0.0.1:8000"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def browser():
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
browser.close()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def page(browser):
|
||||
page = browser.new_page()
|
||||
yield page
|
||||
page.close()
|
||||
|
||||
|
||||
def test_upload_page_loads(page: Page):
|
||||
"""验证上传页面正常加载"""
|
||||
page.goto(BASE_URL)
|
||||
expect(page).to_have_title("COBOL → Java Migration Verification")
|
||||
# 标题包含 verify 文字
|
||||
expect(page.locator("h1")).to_contain_text("verify")
|
||||
# 表单存在
|
||||
form = page.locator("#verify-form")
|
||||
expect(form).to_be_visible()
|
||||
|
||||
|
||||
def test_form_elements_present(page: Page):
|
||||
"""验证所有表单元素存在"""
|
||||
page.goto(BASE_URL)
|
||||
# 4 个文件输入
|
||||
expect(page.locator("input[name=copybook]")).to_be_visible()
|
||||
expect(page.locator("input[name=cobol_src]")).to_be_visible()
|
||||
expect(page.locator("input[name=java_src]")).to_be_visible()
|
||||
expect(page.locator("input[name=mapping]")).to_be_visible()
|
||||
# Runner 下拉框
|
||||
expect(page.locator("select[name=runner]")).to_be_visible()
|
||||
expect(page.locator("select[name=runner]")).to_have_value("native")
|
||||
# 提交按钮
|
||||
expect(page.locator("button[type=submit]")).to_be_visible()
|
||||
expect(page.locator("button[type=submit]")).to_contain_text("verify")
|
||||
|
||||
|
||||
def test_submit_empty_form(page: Page):
|
||||
"""验证空表单提交返回 422 (缺少必填字段)"""
|
||||
page.goto(BASE_URL)
|
||||
result = page.evaluate("""
|
||||
(async () => {
|
||||
const fd = new FormData();
|
||||
const r = await fetch('http://127.0.0.1:8000/verify', { method: 'POST', body: fd });
|
||||
return r.status;
|
||||
})()
|
||||
""")
|
||||
assert result == 422
|
||||
|
||||
|
||||
def test_submit_with_files(page: Page):
|
||||
"""验证上传测试文件后表单正常响应"""
|
||||
page.goto(BASE_URL)
|
||||
page.set_input_files("input[name=copybook]",
|
||||
"tests/fixtures/simple.cpy")
|
||||
page.set_input_files("input[name=cobol_src]",
|
||||
"tests/fixtures/simple.cbl")
|
||||
page.set_input_files("input[name=mapping]",
|
||||
"tests/fixtures/simple.yaml")
|
||||
# 用 evaluate 直接调 API 绕过 webkitdirectory 限制
|
||||
result = page.evaluate("""
|
||||
(async () => {
|
||||
const fd = new FormData();
|
||||
fd.append('copybook', new Blob(['test'], {type:'text/plain'}), 'test.cpy');
|
||||
fd.append('cobol_src', new Blob(['test'], {type:'text/plain'}), 'test.cbl');
|
||||
fd.append('java_src', new Blob(['test'], {type:'text/plain'}), 'test.java');
|
||||
fd.append('mapping', new Blob(['test'], {type:'text/plain'}), 'test.yaml');
|
||||
fd.append('runner', 'native');
|
||||
const r = await fetch('http://127.0.0.1:8000/verify', { method: 'POST', body: fd });
|
||||
return { status: r.status, body: await r.json() };
|
||||
})()
|
||||
""")
|
||||
assert result["status"] == 202
|
||||
assert "task_id" in result["body"]
|
||||
|
||||
|
||||
def test_runner_selector_options(page: Page):
|
||||
"""验证 Runner 下拉框有两个选项"""
|
||||
page.goto(BASE_URL)
|
||||
expect(page.locator("select[name=runner]")).to_be_visible()
|
||||
count = page.locator("select[name=runner] option").count()
|
||||
assert count == 2
|
||||
native_val = page.locator("select[name=runner] option").nth(0).get_attribute("value")
|
||||
spark_val = page.locator("select[name=runner] option").nth(1).get_attribute("value")
|
||||
assert native_val == "native"
|
||||
assert spark_val == "spark"
|
||||
|
||||
|
||||
def test_status_endpoint(page: Page):
|
||||
"""验证 /status/ 端点返回 JSON"""
|
||||
page.goto(f"{BASE_URL}/status/nonexistent")
|
||||
body = page.locator("body").inner_text()
|
||||
assert "404" in body or "not found" in body.lower()
|
||||
|
||||
|
||||
def test_result_endpoint_404(page: Page):
|
||||
"""验证 /result/ 端点对不存在任务返回 404"""
|
||||
page.goto(f"{BASE_URL}/result/nonexistent")
|
||||
body = page.locator("body").inner_text()
|
||||
assert "404" in body or "not found" in body.lower()
|
||||
|
||||
|
||||
def test_dark_theme_rendered(page: Page):
|
||||
"""验证 Terminal Dark 主题渲染"""
|
||||
page.goto(BASE_URL)
|
||||
expect(page.locator(".badge")).to_be_visible()
|
||||
expect(page.locator("footer")).to_be_visible()
|
||||
|
||||
|
||||
def test_page_title(page: Page):
|
||||
"""验证页面标题"""
|
||||
page.goto(BASE_URL)
|
||||
expect(page).to_have_title("COBOL → Java Migration Verification")
|
||||
Reference in New Issue
Block a user