229 lines
8.7 KiB
Python
229 lines
8.7 KiB
Python
import re
|
||
|
||
import pytest
|
||
from playwright.async_api import async_playwright, expect
|
||
|
||
from common.base_page_test import BasePageTest
|
||
from common.config import Config
|
||
|
||
|
||
class GenTableTest(BasePageTest):
|
||
"""代码生成业务测试"""
|
||
|
||
async def test_gen_code_flow(self) -> None:
|
||
"""测试代码生成全流程:导入 -> 编辑 -> 预览 -> 删除"""
|
||
table_name = 'sys_post'
|
||
|
||
# 1. 导航到代码生成页面
|
||
await self.navigate_to_gen_page()
|
||
|
||
# 确保环境清理 (如果存在 sys_post 则先删除)
|
||
await self.ensure_table_not_exists(table_name)
|
||
|
||
# 2. 导入表
|
||
await self.import_table(table_name)
|
||
|
||
# 3. 编辑表
|
||
await self.edit_table(table_name, '测试生成')
|
||
|
||
# 4. 预览代码
|
||
await self.preview_code(table_name)
|
||
await self.page.wait_for_timeout(1000)
|
||
|
||
# 5. 删除表 (清理环境)
|
||
await self.delete_table(table_name)
|
||
|
||
async def navigate_to_gen_page(self) -> None:
|
||
"""导航到代码生成页面"""
|
||
await self.goto_page(Config.frontend_url + '/tool/gen')
|
||
await self.wait_for_selector('.app-container')
|
||
|
||
async def ensure_table_not_exists(self, table_name: str) -> None:
|
||
"""确保表不存在 (如果存在则删除)"""
|
||
await self.delete_table(table_name, verify=False)
|
||
|
||
async def search_table(self, table_name: str) -> None:
|
||
"""搜索表"""
|
||
# 确保在主搜索表单中搜索
|
||
search_form = self.page.locator('.el-form').first
|
||
await search_form.get_by_role('textbox', name='表名称').fill(table_name)
|
||
await search_form.get_by_role('button', name='搜索').click()
|
||
# 等待加载
|
||
loading = self.page.locator('.el-loading-mask')
|
||
if await loading.count() > 0:
|
||
await expect(loading.first).to_be_hidden(timeout=10000)
|
||
await self.page.wait_for_timeout(300)
|
||
|
||
async def import_table(self, table_name: str) -> None:
|
||
"""导入表"""
|
||
await self.page.get_by_role('button', name='导入').click()
|
||
|
||
dialog = self.page.locator("div[role='dialog'][aria-label='导入表']")
|
||
await dialog.wait_for()
|
||
|
||
# 搜索要导入的表
|
||
await dialog.get_by_placeholder('请输入表名称').fill(table_name)
|
||
await dialog.get_by_role('button', name='搜索').click()
|
||
|
||
# 等待搜索结果
|
||
await dialog.locator(f"tr:has-text('{table_name}')").wait_for()
|
||
|
||
# 选中行
|
||
row = dialog.locator('tr').filter(has=self.page.get_by_text(table_name, exact=True))
|
||
|
||
# 点击复选框
|
||
checkbox = row.locator('.el-checkbox')
|
||
await checkbox.click()
|
||
|
||
# 验证已选中
|
||
# Element Plus checkbox 选中时,最外层 label.el-checkbox 会有 is-checked 类
|
||
await expect(checkbox).to_have_class(re.compile(r'is-checked'))
|
||
|
||
await self.page.wait_for_timeout(500)
|
||
|
||
await dialog.get_by_role('button', name='确 定').click()
|
||
|
||
# 检查是否有"请选择要导入的表"错误
|
||
try:
|
||
await expect(self.page.get_by_text('请选择要导入的表')).to_be_visible(timeout=2000)
|
||
print("ERROR: Selection failed, '请选择要导入的表' appeared.")
|
||
except AssertionError:
|
||
pass
|
||
|
||
# 等待一会,让弹窗自动关闭
|
||
await self.page.wait_for_timeout(5000)
|
||
|
||
# 如果弹窗还在,尝试关闭它以免阻塞后续操作
|
||
if await dialog.is_visible():
|
||
print('WARNING: Import dialog still visible after timeout. Forcing close.')
|
||
# Check for error messages
|
||
if await self.page.locator('.el-message--error').count() > 0:
|
||
msg = await self.page.locator('.el-message--error').all_inner_texts()
|
||
print(f'ERROR MESSAGE: {msg}')
|
||
|
||
# 点击取消关闭弹窗
|
||
await dialog.get_by_role('button', name='取 消').click()
|
||
await expect(dialog).to_be_hidden()
|
||
|
||
# 手动刷新列表
|
||
await self.page.get_by_role('button', name='搜索').click()
|
||
|
||
# 验证导入成功 (搜索并在列表中看到)
|
||
# 使用 .app-container 限定在主页面表格,避免匹配到弹窗中的隐藏行
|
||
await self.search_table(table_name)
|
||
row = self.page.locator('.app-container .el-table__body-wrapper tbody tr').filter(
|
||
has=self.page.get_by_text(table_name, exact=True)
|
||
)
|
||
for _i in range(5):
|
||
try:
|
||
await expect(row.first).to_be_visible(timeout=3000)
|
||
break
|
||
except AssertionError:
|
||
await self.search_table(table_name)
|
||
await expect(row.first).to_be_visible(timeout=5000)
|
||
|
||
async def edit_table(self, table_name: str, remark: str) -> None:
|
||
"""编辑表"""
|
||
await self.search_table(table_name)
|
||
row = self.page.locator(f"tbody tr:has-text('{table_name}')")
|
||
|
||
# 点击编辑 (操作列第2个按钮,索引1)
|
||
# 按钮顺序: 预览, 编辑, 删除, 同步, 生成
|
||
await row.locator('button').nth(1).click()
|
||
|
||
# 等待编辑页面 (tab页)
|
||
await self.page.wait_for_selector("div[role='tablist']")
|
||
|
||
# 修改基本信息 -> 表描述
|
||
# 确保在基本信息 Tab
|
||
await self.page.get_by_text('基本信息').click()
|
||
await self.page.get_by_role('textbox', name='表描述').fill(remark)
|
||
|
||
# 提交
|
||
await self.page.get_by_role('button', name='提交').click()
|
||
|
||
# 验证回到列表
|
||
await self.wait_for_selector('.app-container')
|
||
# 验证描述已更新
|
||
await self.search_table(table_name)
|
||
await self.wait_for_selector(f"tbody tr:has-text('{remark}')")
|
||
|
||
async def preview_code(self, table_name: str) -> None:
|
||
"""预览代码"""
|
||
await self.search_table(table_name)
|
||
row = self.page.locator(f"tbody tr:has-text('{table_name}')")
|
||
|
||
# 点击预览 (操作列第1个按钮,索引0)
|
||
await row.locator('button').nth(0).click()
|
||
|
||
# 等待预览弹窗
|
||
dialog = self.page.locator("div[role='dialog'][aria-label='代码预览']")
|
||
await dialog.wait_for()
|
||
|
||
# 验证存在代码内容
|
||
# pre 可能有多个(多tab),只检查可见的
|
||
await dialog.locator('pre:visible').first.wait_for()
|
||
content = await dialog.locator('pre:visible').first.text_content()
|
||
assert 'class' in content or 'import' in content or 'package' in content
|
||
|
||
# 关闭预览 (点击右上角关闭按钮)
|
||
await dialog.locator('.el-dialog__headerbtn').click()
|
||
|
||
async def delete_table(self, table_name: str, verify: bool = True) -> None:
|
||
"""删除表 (支持删除多条重复数据)"""
|
||
# 循环删除直到不存在
|
||
for _i in range(5):
|
||
await self.search_table(table_name)
|
||
# 使用 strict matching
|
||
# 限制在 .app-container .el-table__body-wrapper 以避免固定列导致的重复 以及 避免匹配到弹窗中的行
|
||
row = self.page.locator('.app-container .el-table__body-wrapper tbody tr').filter(
|
||
has=self.page.get_by_text(table_name, exact=True)
|
||
)
|
||
|
||
count = await row.count()
|
||
|
||
if count == 0:
|
||
break
|
||
|
||
# 针对第一行操作
|
||
# 使用 force=True 确保点击,防止遮挡
|
||
btns = row.first.locator('button')
|
||
await btns.nth(2).click(force=True)
|
||
|
||
# 处理确认弹窗
|
||
await self.page.get_by_role('button', name='确定').click()
|
||
|
||
# 等待删除成功提示
|
||
# 使用 specific selector 避免匹配到代码预览中的文本
|
||
await expect(self.page.locator('.el-message__content').filter(has_text='删除成功')).to_be_visible(
|
||
timeout=5000
|
||
)
|
||
|
||
# 等待提示消失,防止干扰下一次操作
|
||
await expect(self.page.locator('.el-message__content').filter(has_text='删除成功')).to_be_hidden(
|
||
timeout=5000
|
||
)
|
||
|
||
if verify:
|
||
# 验证删除成功
|
||
# 重新搜索验证不存在
|
||
await self.search_table(table_name)
|
||
# 使用 strict matching
|
||
row = self.page.locator('.app-container .el-table__body-wrapper tbody tr').filter(
|
||
has=self.page.get_by_text(table_name, exact=True)
|
||
)
|
||
# 期望找不到或者 count为0
|
||
await expect(row).to_have_count(0)
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_gen_table_page() -> None:
|
||
"""测试代码生成页面功能"""
|
||
async with async_playwright() as p:
|
||
test_instance = GenTableTest()
|
||
await test_instance.setup(p)
|
||
try:
|
||
await test_instance.test_gen_code_flow()
|
||
finally:
|
||
await test_instance.teardown()
|