""" 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")