| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- import { TIMEOUTS } from '../../utils/timeouts';
- import { test, expect } from '../../utils/test-setup';
- import { readFileSync } from 'fs';
- import { join, dirname } from 'path';
- import { fileURLToPath } from 'url';
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = dirname(__filename);
- const testUsers = JSON.parse(readFileSync(join(__dirname, '../../fixtures/test-users.json'), 'utf-8'));
- /**
- * 生成唯一区域名称
- * @param prefix - 名称前缀
- * @returns 唯一的区域名称
- */
- function generateUniqueRegionName(prefix: string = '测试区域'): string {
- const timestamp = Date.now();
- const random = Math.floor(Math.random() * 1000);
- return `${prefix}_${timestamp}_${random}`;
- }
- /**
- * 生成唯一区域代码
- * @param level - 区域层级
- * @returns 唯一的区域代码
- */
- function generateUniqueRegionCode(level: string): string {
- const timestamp = Date.now();
- return `${level.toUpperCase()}_${timestamp}`;
- }
- test.describe.serial('编辑区域测试', () => {
- // 用于跟踪测试创建的区域,以便清理
- const createdProvinces: string[] = [];
- test.beforeEach(async ({ adminLoginPage, regionManagementPage }) => {
- // 以管理员身份登录后台
- await adminLoginPage.goto();
- await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
- await adminLoginPage.expectLoginSuccess();
- await regionManagementPage.goto();
- await regionManagementPage.waitForTreeLoaded();
- });
- test.afterEach(async ({ regionManagementPage }) => {
- // 清理测试创建的数据
- let cleanupSuccessCount = 0;
- let cleanupFailCount = 0;
- for (const provinceName of createdProvinces) {
- try {
- // 等待树形结构就绪后检查区域是否存在
- await regionManagementPage.waitForTreeLoaded();
- const exists = await regionManagementPage.regionExists(provinceName);
- if (exists) {
- const deleteSuccess = await regionManagementPage.deleteRegion(provinceName);
- if (deleteSuccess) {
- cleanupSuccessCount++;
- console.debug(`✅ 已清理测试数据: ${provinceName}`);
- } else {
- cleanupFailCount++;
- console.debug(`⚠️ 删除失败(无成功提示): ${provinceName}`);
- }
- } else {
- console.debug(`ℹ️ 区域不存在,跳过删除: ${provinceName}`);
- }
- } catch (error) {
- cleanupFailCount++;
- console.debug(`❌ 清理异常: ${provinceName}`, error);
- }
- }
- // 记录清理结果摘要
- console.debug(`🧹 测试数据清理: 成功 ${cleanupSuccessCount}, 失败 ${cleanupFailCount}`);
- // 如果有清理失败,记录警告但不阻塞测试
- if (cleanupFailCount > 0) {
- console.debug(`⚠️ 有 ${cleanupFailCount} 个区域清理失败,可能产生脏数据`);
- }
- // 清空跟踪列表
- createdProvinces.length = 0;
- });
- test.describe('编辑区域名称', () => {
- test('应该成功编辑区域名称', async ({ regionManagementPage }) => {
- // 首先创建一个测试省份
- const originalName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: originalName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(originalName);
- // 组件会自动刷新省级数据(React Query invalidateQueries)
- await page.waitForTimeout(TIMEOUTS.LONG);
- // 编辑区域名称
- const newName = generateUniqueRegionName('编辑后的省');
- const result = await regionManagementPage.editRegion(originalName, {
- name: newName,
- });
- // 验证编辑成功
- expect(result.success).toBe(true);
- expect(result.hasError).toBe(false);
- // 等待树形结构刷新
- await regionManagementPage.waitForTreeLoaded();
- // 验证列表中显示新名称
- const exists = await regionManagementPage.regionExists(newName);
- expect(exists).toBe(true);
- // 更新清理列表中的名称
- const index = createdProvinces.indexOf(originalName);
- if (index > -1) {
- createdProvinces[index] = newName;
- }
- });
- test('编辑后原名称不应存在', async ({ regionManagementPage }) => {
- const originalName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: originalName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(originalName);
- // 组件会自动刷新省级数据(React Query invalidateQueries)
- await page.waitForTimeout(TIMEOUTS.LONG);
- const newName = generateUniqueRegionName('编辑后的省');
- const result = await regionManagementPage.editRegion(originalName, { name: newName });
- expect(result.success).toBe(true);
- await regionManagementPage.waitForTreeLoaded();
- // 验证原名称不存在
- const originalExists = await regionManagementPage.regionExists(originalName);
- expect(originalExists).toBe(false);
- // 验证新名称存在
- const newExists = await regionManagementPage.regionExists(newName);
- expect(newExists).toBe(true);
- // 更新清理列表
- const index = createdProvinces.indexOf(originalName);
- if (index > -1) {
- createdProvinces[index] = newName;
- }
- });
- });
- test.describe('修改区域代码', () => {
- test('应该成功修改行政区划代码', async ({ regionManagementPage }) => {
- const provinceName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: provinceName,
- code: 'OLD_CODE',
- level: 1,
- });
- createdProvinces.push(provinceName);
- await regionManagementPage.waitForTreeLoaded();
- // 修改代码
- const newCode = generateUniqueRegionCode('NEW');
- const result = await regionManagementPage.editRegion(provinceName, {
- code: newCode,
- });
- expect(result.success).toBe(true);
- expect(result.hasError).toBe(false);
- });
- test('应该能同时修改名称和代码', async ({ regionManagementPage }) => {
- const originalName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: originalName,
- code: 'OLD_CODE',
- level: 1,
- });
- createdProvinces.push(originalName);
- await regionManagementPage.waitForTreeLoaded();
- const newName = generateUniqueRegionName('新省名');
- const newCode = generateUniqueRegionCode('NEW');
- const result = await regionManagementPage.editRegion(originalName, {
- name: newName,
- code: newCode,
- });
- expect(result.success).toBe(true);
- expect(result.hasError).toBe(false);
- await regionManagementPage.waitForTreeLoaded();
- expect(await regionManagementPage.regionExists(newName)).toBe(true);
- // 更新清理列表
- const index = createdProvinces.indexOf(originalName);
- if (index > -1) {
- createdProvinces[index] = newName;
- }
- });
- });
- test.describe('区域状态切换', () => {
- test('应该成功禁用已启用的区域', async ({ regionManagementPage }) => {
- const provinceName = generateUniqueRegionName('测试省');
- console.debug(`创建省份: ${provinceName}`);
- await regionManagementPage.createProvince({
- name: provinceName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(provinceName);
- // 等待树形结构加载完成
- await regionManagementPage.waitForTreeLoaded();
- // 验证区域存在并获取初始状态
- const exists = await regionManagementPage.regionExists(provinceName);
- expect(exists).toBe(true);
- const initialStatus = await regionManagementPage.getRegionStatus(provinceName);
- expect(initialStatus).toBe('启用');
- // 禁用区域
- const success = await regionManagementPage.toggleRegionStatus(provinceName);
- expect(success).toBe(true);
- // 等待树形结构刷新(toggleRegionStatus 内部已等待)
- await regionManagementPage.waitForTreeLoaded();
- // 验证状态已更新
- const newStatus = await regionManagementPage.getRegionStatus(provinceName);
- expect(newStatus).toBe('禁用');
- });
- test('应该成功启用已禁用的区域', async ({ regionManagementPage }) => {
- const provinceName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: provinceName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(provinceName);
- await regionManagementPage.waitForTreeLoaded();
- // 先禁用
- await regionManagementPage.toggleRegionStatus(provinceName);
- await regionManagementPage.waitForTreeLoaded();
- // 验证已禁用
- const disabledStatus = await regionManagementPage.getRegionStatus(provinceName);
- expect(disabledStatus).toBe('禁用');
- // 再启用
- const success = await regionManagementPage.toggleRegionStatus(provinceName);
- expect(success).toBe(true);
- await regionManagementPage.waitForTreeLoaded();
- // 验证状态已恢复为启用
- const status = await regionManagementPage.getRegionStatus(provinceName);
- expect(status).toBe('启用');
- });
- test('取消状态切换应保持原状态', async ({ regionManagementPage }) => {
- const provinceName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: provinceName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(provinceName);
- await regionManagementPage.waitForTreeLoaded();
- const initialStatus = await regionManagementPage.getRegionStatus(provinceName);
- expect(initialStatus).toBe('启用');
- // 打开状态切换对话框但取消
- await regionManagementPage.openToggleStatusDialog(provinceName);
- await regionManagementPage.cancelToggleStatus();
- // 验证状态未改变(不需要刷新页面)
- const currentStatus = await regionManagementPage.getRegionStatus(provinceName);
- expect(currentStatus).toBe(initialStatus);
- });
- });
- test.describe.skip('编辑子区域 - TODO: 需要修复 createChildRegion 功能', () => {
- test('应该成功编辑市级区域名称', async ({ regionManagementPage, page }) => {
- const provinceName = generateUniqueRegionName('测试省');
- const originalCityName = generateUniqueRegionName('测试市');
- // 创建省和市
- await regionManagementPage.createProvince({
- name: provinceName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(provinceName);
- await page.goto('/admin/areas');
- await regionManagementPage.waitForTreeLoaded();
- // 创建市后,先展开省节点验证市已创建
- const cityResult = await regionManagementPage.createChildRegion(provinceName, '市', {
- name: originalCityName,
- code: generateUniqueRegionCode('CITY'),
- level: 2,
- });
- expect(cityResult.success).toBe(true);
- await page.goto('/admin/areas');
- await regionManagementPage.waitForTreeLoaded();
- // 直接展开新创建的省节点(滚动到可见区域)
- await regionManagementPage.expandNode(provinceName);
- await page.waitForTimeout(TIMEOUTS.LONG);
- // 验证市级区域可见
- const cityVisible = await regionManagementPage.regionExists(originalCityName);
- console.debug(`市级区域 "${originalCityName}" 可见: ${cityVisible}`);
- expect(cityVisible).toBe(true);
- // 编辑城市名称
- const newCityName = generateUniqueRegionName('编辑后的市');
- const result = await regionManagementPage.editRegion(originalCityName, {
- name: newCityName,
- });
- expect(result.success).toBe(true);
- expect(result.hasError).toBe(false);
- });
- test('应该成功编辑区级区域状态', async ({ regionManagementPage, page }) => {
- const provinceName = generateUniqueRegionName('测试省');
- const cityName = generateUniqueRegionName('测试市');
- const districtName = generateUniqueRegionName('测试区');
- // 创建省市区三级结构
- await regionManagementPage.createProvince({
- name: provinceName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(provinceName);
- await page.goto('/admin/areas');
- await regionManagementPage.waitForTreeLoaded();
- const cityResult = await regionManagementPage.createChildRegion(provinceName, '市', {
- name: cityName,
- code: generateUniqueRegionCode('CITY'),
- level: 2,
- });
- expect(cityResult.success).toBe(true);
- await page.goto('/admin/areas');
- await regionManagementPage.waitForTreeLoaded();
- const districtResult = await regionManagementPage.createChildRegion(provinceName, '市', {
- name: districtName,
- code: generateUniqueRegionCode('DISTRICT'),
- level: 3,
- });
- expect(districtResult.success).toBe(true);
- await page.goto('/admin/areas');
- await regionManagementPage.waitForTreeLoaded();
- // 展开省节点
- await regionManagementPage.expandNode(provinceName);
- await page.waitForTimeout(TIMEOUTS.LONG);
- // 切换区的状态
- const success = await regionManagementPage.toggleRegionStatus(districtName);
- expect(success).toBe(true);
- });
- });
- test.describe('表单验证', () => {
- test('清空名称时应显示错误提示', async ({ regionManagementPage }) => {
- const provinceName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: provinceName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(provinceName);
- await regionManagementPage.waitForTreeLoaded();
- // 打开编辑对话框并清空名称
- await regionManagementPage.openEditDialog(provinceName);
- await regionManagementPage.page.getByLabel('区域名称').fill('');
- // 提交表单
- const submitButton = regionManagementPage.page.getByRole('button', { name: '更新' });
- await submitButton.click();
- // 验证错误提示 - 可能是内联错误或 toast
- await regionManagementPage.page.waitForTimeout(TIMEOUTS.MEDIUM);
- // 检查内联错误
- const nameError = regionManagementPage.page.getByText('区域名称不能为空');
- const hasInlineError = await nameError.count() > 0;
- // 检查 toast 错误
- const errorToast = regionManagementPage.page.locator('[data-sonner-toast][data-type="error"]');
- const hasToastError = await errorToast.count() > 0;
- // 至少应该有一种错误提示
- expect(hasInlineError || hasToastError).toBe(true);
- // 取消对话框
- await regionManagementPage.cancelDialog();
- });
- test('应该支持取消编辑操作', async ({ regionManagementPage }) => {
- const provinceName = generateUniqueRegionName('测试省');
- await regionManagementPage.createProvince({
- name: provinceName,
- code: generateUniqueRegionCode('PROV'),
- level: 1,
- });
- createdProvinces.push(provinceName);
- await regionManagementPage.waitForTreeLoaded();
- // 打开编辑对话框
- await regionManagementPage.openEditDialog(provinceName);
- // 修改名称
- const newName = generateUniqueRegionName('修改后的省');
- await regionManagementPage.page.getByLabel('区域名称').fill(newName);
- // 点击取消按钮
- await regionManagementPage.cancelDialog();
- // 验证对话框已关闭
- const dialog = regionManagementPage.page.locator('[role="dialog"]');
- await expect(dialog).not.toBeVisible();
- // 验证数据未修改 - 原名称仍存在
- await regionManagementPage.waitForTreeLoaded();
- const originalExists = await regionManagementPage.regionExists(provinceName);
- expect(originalExists).toBe(true);
- });
- });
- test.describe('连续编辑操作', () => {
- test('应该能连续编辑多个区域', async ({ regionManagementPage }) => {
- // 创建多个省份
- const province1 = generateUniqueRegionName('测试省1');
- const province2 = generateUniqueRegionName('测试省2');
- await regionManagementPage.createProvince({
- name: province1,
- code: generateUniqueRegionCode('PROV1'),
- level: 1,
- });
- createdProvinces.push(province1);
- await regionManagementPage.waitForTreeLoaded();
- await regionManagementPage.createProvince({
- name: province2,
- code: generateUniqueRegionCode('PROV2'),
- level: 1,
- });
- createdProvinces.push(province2);
- await regionManagementPage.waitForTreeLoaded();
- // 编辑第一个省份
- const newProvince1Name = generateUniqueRegionName('编辑后的省1');
- const result1 = await regionManagementPage.editRegion(province1, { name: newProvince1Name });
- expect(result1.success).toBe(true);
- // 更新清理列表
- const index1 = createdProvinces.indexOf(province1);
- if (index1 > -1) {
- createdProvinces[index1] = newProvince1Name;
- }
- // 编辑第二个省份
- const newProvince2Name = generateUniqueRegionName('编辑后的省2');
- const result2 = await regionManagementPage.editRegion(province2, { name: newProvince2Name });
- expect(result2.success).toBe(true);
- // 更新清理列表
- const index2 = createdProvinces.indexOf(province2);
- if (index2 > -1) {
- createdProvinces[index2] = newProvince2Name;
- }
- });
- });
- });
|