mirror of
https://github.com/insistence/RuoYi-Vue3-FastAPI.git
synced 2026-10-14 13:06:46 +00:00
* refactor: 重构项目架构 * refactor: 重构ci和测试 * fix: 修复测试 * feat: 新增更多E2E测试 * fix: 修复E2E测试
165 lines
8.1 KiB
Python
165 lines
8.1 KiB
Python
import asyncio
|
|
import re
|
|
from http import HTTPStatus
|
|
from pathlib import Path
|
|
from urllib.parse import urlparse
|
|
from uuid import uuid4
|
|
from zipfile import ZipFile
|
|
|
|
import pytest
|
|
from playwright.async_api import Locator, expect
|
|
|
|
from common.base_page_test import BasePageTest
|
|
from common.browser_harness import BrowserHarness
|
|
from common.config import Config
|
|
|
|
pytestmark = pytest.mark.e2e
|
|
|
|
|
|
class GenTableTest(BasePageTest):
|
|
"""只操作本用例导入的生成配置,验证编辑、代码预览及下载。"""
|
|
|
|
table_id: int | None = None
|
|
table_name: str = ''
|
|
table_comment: str = ''
|
|
|
|
def table_row(self) -> Locator:
|
|
return self.page.locator('.app-container:visible > .el-table .el-table__body-wrapper tbody tr').filter(
|
|
has=self.page.get_by_text(self.table_name, exact=True)
|
|
)
|
|
|
|
async def search_table(self) -> dict:
|
|
"""等待搜索接口与表格完成,避免初始响应覆盖查询结果。"""
|
|
form = self.page.locator('form').first
|
|
await form.locator('.el-form-item').filter(has_text='表名称').locator('input').fill(self.table_name)
|
|
async with self.page.expect_response(
|
|
lambda response: urlparse(response.url).path.endswith('/tool/gen/list')
|
|
) as pending:
|
|
await form.get_by_role('button', name=re.compile(r'搜索$')).click()
|
|
payload = await (await pending.value).json()
|
|
assert payload['code'] == HTTPStatus.OK, payload
|
|
await expect(self.page.locator('.el-table .el-loading-mask:visible')).to_have_count(0)
|
|
return payload
|
|
|
|
async def import_table(self) -> None:
|
|
"""从未导入的真实表中选择一张,不删除任何既有生成配置。"""
|
|
async with self.page.expect_response(
|
|
lambda response: urlparse(response.url).path.endswith('/tool/gen/db/list')
|
|
) as pending:
|
|
await self.page.get_by_role('button', name=re.compile(r'导入$')).click()
|
|
payload = await (await pending.value).json()
|
|
assert payload['code'] == HTTPStatus.OK and payload['rows'], payload
|
|
self.table_name = payload['rows'][0]['tableName']
|
|
dialog = self.page.get_by_role('dialog', name='导入表', exact=True)
|
|
row = dialog.locator('tbody tr').filter(has=self.page.get_by_text(self.table_name, exact=True))
|
|
await row.locator('.el-checkbox').click()
|
|
await expect(row.locator('.el-checkbox')).to_have_class(re.compile('is-checked'))
|
|
async with self.page.expect_response(
|
|
lambda response: (
|
|
response.request.method == 'POST' and urlparse(response.url).path.endswith('/tool/gen/importTable')
|
|
)
|
|
) as pending:
|
|
await dialog.get_by_role('button', name=re.compile(r'确\s*定$')).click()
|
|
result = await (await pending.value).json()
|
|
assert result['code'] == HTTPStatus.OK, result
|
|
await expect(dialog).to_be_hidden()
|
|
listed = await self.search_table()
|
|
assert len(listed['rows']) == 1, listed
|
|
self.table_id = listed['rows'][0]['tableId']
|
|
await expect(self.table_row()).to_have_count(1)
|
|
|
|
async def edit_table(self) -> None:
|
|
"""修改实际生成配置,并重新读取确认服务端持久化结果。"""
|
|
await self.table_row().locator('td').last.get_by_role('button').nth(1).click()
|
|
await self.page.wait_for_url(f'**/tool/gen-edit/index/{self.table_id}**')
|
|
self.table_comment = f'生成验收{uuid4().hex[:8]}'
|
|
await self.page.get_by_role('tab', name='基本信息', exact=True).click()
|
|
await (
|
|
self.page.locator('.el-form-item:visible')
|
|
.filter(has_text='表描述')
|
|
.locator('input')
|
|
.fill(self.table_comment)
|
|
)
|
|
async with self.page.expect_response(
|
|
lambda response: response.request.method == 'PUT' and urlparse(response.url).path.endswith('/tool/gen')
|
|
) as pending:
|
|
await self.page.get_by_role('button', name=re.compile(r'提交$')).click()
|
|
result = await (await pending.value).json()
|
|
assert result['code'] == HTTPStatus.OK, result
|
|
await self.page.wait_for_url(re.compile(r'/tool/gen(?:\?.*)?$'))
|
|
listed = await self.search_table()
|
|
assert listed['rows'][0]['tableComment'] == self.table_comment
|
|
await expect(self.table_row()).to_contain_text(self.table_comment)
|
|
|
|
async def preview_and_download(self) -> None:
|
|
"""核对预览接口、可见 Python 代码和实际下载 ZIP 的内容。"""
|
|
async with self.page.expect_response(
|
|
lambda response: urlparse(response.url).path.endswith(f'/tool/gen/preview/{self.table_id}')
|
|
) as pending:
|
|
await self.table_row().locator('td').last.get_by_role('button').nth(0).click()
|
|
payload = await (await pending.value).json()
|
|
assert payload['code'] == HTTPStatus.OK, payload
|
|
code = payload['data']
|
|
assert any(name.endswith('do.py.jinja2') for name in code), code.keys()
|
|
assert any(self.table_comment in source for source in code.values())
|
|
dialog = self.page.get_by_role('dialog', name='代码预览', exact=True)
|
|
await expect(dialog.locator('pre:visible').first).to_contain_text('class')
|
|
await expect(dialog.locator('pre:visible').first).to_contain_text(self.table_comment)
|
|
await dialog.locator('.el-dialog__headerbtn').click()
|
|
async with self.page.expect_download() as pending:
|
|
await self.table_row().locator('td').last.get_by_role('button').nth(4).click()
|
|
download = await pending.value
|
|
assert download.suggested_filename.endswith('.zip')
|
|
zip_path = Path(await download.path())
|
|
|
|
def read_generated_code() -> tuple[list[str], str]:
|
|
with ZipFile(zip_path) as archive:
|
|
names = archive.namelist()
|
|
return names, '\n'.join(archive.read(name).decode('utf-8') for name in names if name.endswith('.py'))
|
|
|
|
names, generated = await asyncio.to_thread(read_generated_code)
|
|
assert any(name.endswith('.vue') for name in names), names
|
|
assert any(name.endswith('.py') for name in names), names
|
|
assert self.table_comment in generated and self.table_name in generated
|
|
|
|
async def delete_table(self) -> None:
|
|
"""通过界面移除刚创建的元数据,并确认实际业务表仍存在。"""
|
|
await self.table_row().locator('td').last.get_by_role('button').nth(2).click()
|
|
async with self.page.expect_response(
|
|
lambda response: (
|
|
response.request.method == 'DELETE'
|
|
and urlparse(response.url).path.endswith(f'/tool/gen/{self.table_id}')
|
|
)
|
|
) as pending:
|
|
await self.page.get_by_role('button', name=re.compile(r'确定$')).click()
|
|
assert (await (await pending.value).json())['code'] == HTTPStatus.OK
|
|
self.table_id = None
|
|
await self.search_table()
|
|
await expect(self.table_row()).to_have_count(0)
|
|
api = await self.harness.api_context(self.token)
|
|
available = await api.get('/tool/gen/db/list', params={'tableName': self.table_name})
|
|
assert any(row['tableName'] == self.table_name for row in (await available.json())['rows'])
|
|
|
|
async def cleanup(self) -> None:
|
|
"""失败时仍只清理由本次导入取得 ID 的元数据。"""
|
|
if self.table_id is not None:
|
|
api = await self.harness.api_context(self.token)
|
|
response = await api.delete(f'/tool/gen/{self.table_id}')
|
|
assert (await response.json())['code'] == HTTPStatus.OK
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_gen_table_page(browser_harness: BrowserHarness) -> None:
|
|
"""测试真实数据库表的导入、编辑、预览、下载与删除。"""
|
|
test = GenTableTest()
|
|
await test.setup(browser_harness)
|
|
try:
|
|
async with test.page.expect_response(lambda response: urlparse(response.url).path.endswith('/tool/gen/list')):
|
|
await test.page.goto(Config.frontend_url + '/tool/gen')
|
|
await test.import_table()
|
|
await test.edit_table()
|
|
await test.preview_and_download()
|
|
await test.delete_table()
|
|
finally:
|
|
await test.cleanup()
|