diff --git a/e2e/src/tests/admin/case-management.spec.ts b/e2e/src/tests/admin/case-management.spec.ts index 8dd31b1..2af90b8 100644 --- a/e2e/src/tests/admin/case-management.spec.ts +++ b/e2e/src/tests/admin/case-management.spec.ts @@ -22,9 +22,7 @@ test.describe('成功案例管理E2E测试', () => { await adminContentPage.searchContent('测试案例'); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可编辑的案例'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.editContent(0); @@ -46,9 +44,7 @@ test.describe('成功案例管理E2E测试', () => { await adminContentPage.searchContent(caseData.title); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可删除的案例'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.deleteContent(0); diff --git a/e2e/src/tests/admin/news-management.spec.ts b/e2e/src/tests/admin/news-management.spec.ts index 5feb654..0b3f77f 100644 --- a/e2e/src/tests/admin/news-management.spec.ts +++ b/e2e/src/tests/admin/news-management.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '../../fixtures/admin.fixture'; -import { adminTestData, generateTestContent } from '../../data/admin-test-data'; +import { generateTestContent } from '../../data/admin-test-data'; test.describe('新闻动态管理E2E测试', () => { test('应该能够创建新闻', async ({ page, adminContentPage }) => { @@ -43,9 +43,7 @@ test.describe('新闻动态管理E2E测试', () => { await adminContentPage.searchContent('要发布的新闻'); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可编辑的新闻'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.editContent(0); @@ -65,9 +63,7 @@ test.describe('新闻动态管理E2E测试', () => { await adminContentPage.searchContent('测试新闻'); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可编辑的新闻'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.editContent(0); @@ -89,9 +85,7 @@ test.describe('新闻动态管理E2E测试', () => { await adminContentPage.searchContent(newsData.title); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可删除的新闻'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.deleteContent(0); diff --git a/e2e/src/tests/admin/permissions.spec.ts b/e2e/src/tests/admin/permissions.spec.ts index a95aeba..9aaf5fd 100644 --- a/e2e/src/tests/admin/permissions.spec.ts +++ b/e2e/src/tests/admin/permissions.spec.ts @@ -1,5 +1,4 @@ import { test, expect } from '../../fixtures/admin.fixture'; -import { adminTestData } from '../../data/admin-test-data'; test.describe('权限控制E2E测试', () => { test('管理员应该能够创建所有类型的内容', async ({ page, adminContentPage }) => { @@ -21,11 +20,43 @@ test.describe('权限控制E2E测试', () => { }); test('编辑者应该能够创建内容但不能删除', async ({ page, adminContentPage }) => { - test.skip(true, '需要编辑者账户认证'); + await adminContentPage.goto(); + + await expect(adminContentPage.createButton).toBeVisible(); + + const contentData = { + type: 'product', + title: '编辑者创建的产品-' + Date.now(), + slug: 'editor-product-' + Date.now(), + excerpt: '编辑者创建的产品', + content: '

编辑者创建的产品内容

', + category: '软件产品', + tags: ['编辑者测试'], + status: 'draft', + }; + + await adminContentPage.createContent(contentData); + await expect(page.locator('text=保存成功')).toBeVisible({ timeout: 5000 }); + + await adminContentPage.goto(); + await adminContentPage.searchContent(contentData.title); + + const deleteButton = page.locator('button').filter({ hasText: /删除/i }); + await expect(deleteButton).toHaveCount(0); }); test('查看者应该只能查看内容', async ({ page, adminContentPage }) => { - test.skip(true, '需要查看者账户认证'); + await adminContentPage.goto(); + + await expect(adminContentPage.createButton).not.toBeVisible(); + + await expect(adminContentPage.contentList).toBeVisible(); + + const createButton = page.locator('button').filter({ hasText: /创建/i }); + await expect(createButton).toHaveCount(0); + + const deleteButtons = page.locator('button').filter({ hasText: /删除/i }); + await expect(deleteButtons).toHaveCount(0); }); test('未登录用户应该被重定向到登录页', async ({ page }) => { diff --git a/e2e/src/tests/admin/product-management.spec.ts b/e2e/src/tests/admin/product-management.spec.ts index f439553..dd42760 100644 --- a/e2e/src/tests/admin/product-management.spec.ts +++ b/e2e/src/tests/admin/product-management.spec.ts @@ -22,9 +22,7 @@ test.describe('产品服务管理E2E测试', () => { await adminContentPage.searchContent('测试产品'); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可编辑的产品'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.editContent(0); @@ -52,9 +50,7 @@ test.describe('产品服务管理E2E测试', () => { await adminContentPage.searchContent(productData.title); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可删除的产品'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.deleteContent(0); diff --git a/e2e/src/tests/admin/service-management.spec.ts b/e2e/src/tests/admin/service-management.spec.ts index a43955e..643c132 100644 --- a/e2e/src/tests/admin/service-management.spec.ts +++ b/e2e/src/tests/admin/service-management.spec.ts @@ -22,9 +22,7 @@ test.describe('服务管理E2E测试', () => { await adminContentPage.searchContent('测试服务'); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可编辑的服务'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.editContent(0); @@ -46,9 +44,7 @@ test.describe('服务管理E2E测试', () => { await adminContentPage.searchContent(serviceData.title); const initialCount = await adminContentPage.contentList.count(); - if (initialCount === 0) { - test.skip(true, '没有找到可删除的服务'); - } + expect(initialCount).toBeGreaterThan(0, '测试数据未创建,请运行 npm run db:seed:test'); await adminContentPage.deleteContent(0); diff --git a/e2e/src/tests/regression/contact-form.regression.spec.ts b/e2e/src/tests/regression/contact-form.regression.spec.ts index c0e309e..2fdbb69 100644 --- a/e2e/src/tests/regression/contact-form.regression.spec.ts +++ b/e2e/src/tests/regression/contact-form.regression.spec.ts @@ -13,12 +13,10 @@ test.describe('联系表单回归测试 @regression', () => { await contactPage.page.waitForTimeout(5000); const isSuccessVisible = await contactPage.isSuccessMessageVisible(); - console.log('Success message visible:', isSuccessVisible); - expect(isSuccessVisible).toBe(true); }); - test.skip('应该验证必填字段', async ({ contactPage }) => { + test('应该验证必填字段', async ({ contactPage }) => { await contactPage.submitForm(); await contactPage.waitForFormSubmission(); const isSubmitted = await contactPage.isFormSubmitted(); @@ -101,7 +99,7 @@ test.describe('联系表单回归测试 @regression', () => { expect(focusedElement).toBe('INPUT'); }); - test.skip('应该能够使用回车键提交表单', async ({ contactPage, testDataGenerator }) => { + test('应该能够使用回车键提交表单', async ({ contactPage, testDataGenerator }) => { const formData = testDataGenerator.generateContactFormData(); await contactPage.fillContactForm(formData); await contactPage.page.keyboard.press('Enter'); @@ -110,7 +108,7 @@ test.describe('联系表单回归测试 @regression', () => { expect(isSubmitted).toBe(true); }); - test.skip('应该显示提交按钮的加载状态', async ({ contactPage, testDataGenerator }) => { + test('应该显示提交按钮的加载状态', async ({ contactPage, testDataGenerator }) => { const formData = testDataGenerator.generateContactFormData(); await contactPage.fillContactForm(formData); await contactPage.submitButton.click(); diff --git a/package.json b/package.json index b793076..7fef2e4 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio", "db:seed": "tsx src/db/seed.ts", + "db:seed:test": "tsx src/db/seed-test-data.ts", "lighthouse": "lhci autorun", "lighthouse:collect": "lhci collect", "lighthouse:assert": "lhci assert", diff --git a/src/db/seed-test-data.ts b/src/db/seed-test-data.ts new file mode 100644 index 0000000..7eb68b3 --- /dev/null +++ b/src/db/seed-test-data.ts @@ -0,0 +1,307 @@ +import { db } from './index'; +import { content, users } from './schema'; +import { nanoid } from 'nanoid'; +import bcrypt from 'bcryptjs'; +import { eq } from 'drizzle-orm'; + +async function seedTestData() { + /* eslint-disable no-console */ + console.log('🌱 开始创建测试数据...'); + + try { + const existingAdmin = await db + .select() + .from(users) + .where(eq(users.email, 'admin@novalon.cn')) + .limit(1); + + if (existingAdmin.length === 0) { + console.log('❌ 管理员用户不存在,请先运行主种子数据脚本'); + return; + } + + const adminId = existingAdmin[0]!.id; + + const editorEmail = 'editor@novalon.cn'; + const existingEditor = await db + .select() + .from(users) + .where(eq(users.email, editorEmail)) + .limit(1); + + if (existingEditor.length === 0) { + const hashedPassword = await bcrypt.hash('editor123456', 10); + await db.insert(users).values({ + id: nanoid(), + email: editorEmail, + passwordHash: hashedPassword, + name: '内容编辑者', + isAdmin: false, + createdAt: new Date(), + updatedAt: new Date(), + }); + console.log('✅ 创建编辑者用户: editor@novalon.cn'); + } else { + console.log('ℹ️ 编辑者用户已存在,跳过创建'); + } + + const viewerEmail = 'viewer@novalon.cn'; + const existingViewer = await db + .select() + .from(users) + .where(eq(users.email, viewerEmail)) + .limit(1); + + if (existingViewer.length === 0) { + const hashedPassword = await bcrypt.hash('viewer123456', 10); + await db.insert(users).values({ + id: nanoid(), + email: viewerEmail, + passwordHash: hashedPassword, + name: '内容查看者', + isAdmin: false, + createdAt: new Date(), + updatedAt: new Date(), + }); + console.log('✅ 创建查看者用户: viewer@novalon.cn'); + } else { + console.log('ℹ️ 查看者用户已存在,跳过创建'); + } + + const testProducts = [ + { + id: nanoid(), + type: 'product' as const, + title: '测试产品 - ERP系统', + slug: 'test-product-erp', + excerpt: '这是一个测试产品', + content: '

ERP系统测试内容

', + coverImage: '/images/test-product-1.jpg', + category: '软件产品', + tags: ['ERP', '企业管理'], + status: 'published' as const, + publishedAt: new Date(), + authorId: adminId, + metadata: { + features: ['财务管理', '供应链管理', '生产管理'], + benefits: ['提高效率', '降低成本', '优化流程'], + pricing: { + basic: { price: '9999', period: '年' }, + standard: { price: '19999', period: '年' }, + enterprise: { price: '39999', period: '年' }, + }, + }, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: nanoid(), + type: 'product' as const, + title: '测试产品 - CRM系统', + slug: 'test-product-crm', + excerpt: '这是一个测试产品', + content: '

CRM系统测试内容

', + coverImage: '/images/test-product-2.jpg', + category: '软件产品', + tags: ['CRM', '客户管理'], + status: 'published' as const, + publishedAt: new Date(), + authorId: adminId, + metadata: { + features: ['客户管理', '销售管理', '营销管理'], + benefits: ['提升客户满意度', '增加销售业绩', '优化营销策略'], + pricing: { + basic: { price: '5999', period: '年' }, + standard: { price: '12999', period: '年' }, + enterprise: { price: '25999', period: '年' }, + }, + }, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]; + + const testNews = [ + { + id: nanoid(), + type: 'news' as const, + title: '测试新闻 - 公司获得行业大奖', + slug: 'test-news-award', + excerpt: '这是一个测试新闻', + content: '

测试新闻内容

', + coverImage: '/images/test-news-1.jpg', + category: '公司新闻', + tags: ['获奖', '荣誉'], + status: 'published' as const, + publishedAt: new Date(), + authorId: adminId, + metadata: {}, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: nanoid(), + type: 'news' as const, + title: '测试新闻 - 产品发布会', + slug: 'test-news-launch', + excerpt: '这是一个测试新闻', + content: '

测试新闻内容

', + coverImage: '/images/test-news-2.jpg', + category: '产品发布', + tags: ['发布', '新产品'], + status: 'published' as const, + publishedAt: new Date(), + authorId: adminId, + metadata: {}, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]; + + const testCases = [ + { + id: nanoid(), + type: 'case' as const, + title: '测试案例 - 某大型企业数字化转型', + slug: 'test-case-enterprise', + excerpt: '这是一个测试案例', + content: '

测试案例内容

', + coverImage: '/images/test-case-1.jpg', + category: '制造业', + tags: ['数字化转型', '制造业'], + status: 'published' as const, + publishedAt: new Date(), + authorId: adminId, + metadata: { + client: '某大型制造企业', + industry: '制造业', + duration: '6个月', + results: ['效率提升30%', '成本降低20%'], + }, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]; + + const testServices = [ + { + id: nanoid(), + type: 'service' as const, + title: '测试服务 - 软件开发', + slug: 'test-service-software', + excerpt: '这是一个测试服务', + content: '

测试服务内容

', + coverImage: '/images/test-service-1.jpg', + category: '软件开发', + tags: ['开发', '定制'], + status: 'published' as const, + publishedAt: new Date(), + authorId: adminId, + metadata: { + icon: 'Code', + features: ['需求分析', '系统设计', '开发实施', '测试部署'], + benefits: ['专业团队', '质量保证', '按时交付'], + }, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: nanoid(), + type: 'service' as const, + title: '测试服务 - 云服务', + slug: 'test-service-cloud', + excerpt: '这是一个测试服务', + content: '

测试服务内容

', + coverImage: '/images/test-service-2.jpg', + category: '云服务', + tags: ['云', '部署'], + status: 'published' as const, + publishedAt: new Date(), + authorId: adminId, + metadata: { + icon: 'Cloud', + features: ['云迁移', '云部署', '云运维', '云安全'], + benefits: ['高可用性', '弹性扩展', '成本优化'], + }, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]; + + for (const product of testProducts) { + const existing = await db + .select() + .from(content) + .where(eq(content.slug, product.slug)) + .limit(1); + + if (existing.length === 0) { + await db.insert(content).values(product); + console.log(`✅ 创建测试产品: ${product.title}`); + } else { + console.log(`ℹ️ 测试产品已存在,跳过: ${product.title}`); + } + } + + for (const news of testNews) { + const existing = await db + .select() + .from(content) + .where(eq(content.slug, news.slug)) + .limit(1); + + if (existing.length === 0) { + await db.insert(content).values(news); + console.log(`✅ 创建测试新闻: ${news.title}`); + } else { + console.log(`ℹ️ 测试新闻已存在,跳过: ${news.title}`); + } + } + + for (const caseItem of testCases) { + const existing = await db + .select() + .from(content) + .where(eq(content.slug, caseItem.slug)) + .limit(1); + + if (existing.length === 0) { + await db.insert(content).values(caseItem); + console.log(`✅ 创建测试案例: ${caseItem.title}`); + } else { + console.log(`ℹ️ 测试案例已存在,跳过: ${caseItem.title}`); + } + } + + for (const service of testServices) { + const existing = await db + .select() + .from(content) + .where(eq(content.slug, service.slug)) + .limit(1); + + if (existing.length === 0) { + await db.insert(content).values(service); + console.log(`✅ 创建测试服务: ${service.title}`); + } else { + console.log(`ℹ️ 测试服务已存在,跳过: ${service.title}`); + } + } + + console.log('🎉 测试数据创建完成!'); + } catch (error) { + console.error('❌ 测试数据创建失败:', error); + throw error; + } + /* eslint-enable no-console */ +} + +seedTestData() + .then(() => { + console.log('✅ 测试数据脚本执行完成'); + process.exit(0); + }) + .catch((error) => { + console.error('❌ 测试数据脚本执行失败:', error); + process.exit(1); + }); \ No newline at end of file