import { test, expect, Page } from '@playwright/test'; const BASE_URL = process.env.BASE_URL || 'http://localhost:3000'; const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@novalon.cn'; const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; test.describe('后台与前台页面交互测试', () => { test('首页展示所有内容类型入口', async ({ page }) => { await page.goto(BASE_URL); await page.waitForLoadState('networkidle'); const navLinks = page.locator('nav a, header a[href]'); const count = await navLinks.count(); console.log(`首页导航链接数量: ${count}`); expect(count).toBeGreaterThan(0); const linkTexts = await navLinks.allTextContents(); console.log('导航链接:', linkTexts); }); test('新闻页面内容展示', async ({ page }) => { await page.goto(`${BASE_URL}/news`); await page.waitForLoadState('networkidle'); await expect(page).toHaveURL(/\/news/); const mainContent = page.locator('main, [role="main"]'); await expect(mainContent).toBeVisible(); const heading = page.locator('h1, h2').first(); const hasHeading = await heading.isVisible().catch(() => false); console.log(`新闻页面标题${hasHeading ? '存在' : '不存在'}`); }); test('产品页面内容展示', async ({ page }) => { await page.goto(`${BASE_URL}/products`); await page.waitForLoadState('networkidle'); await expect(page).toHaveURL(/\/products/); const mainContent = page.locator('main, [role="main"]'); await expect(mainContent).toBeVisible(); }); test('服务页面内容展示', async ({ page }) => { await page.goto(`${BASE_URL}/services`); await page.waitForLoadState('networkidle'); await expect(page).toHaveURL(/\/services/); const mainContent = page.locator('main, [role="main"]'); await expect(mainContent).toBeVisible(); }); test('案例页面内容展示', async ({ page }) => { await page.goto(`${BASE_URL}/cases`); await page.waitForLoadState('networkidle'); await expect(page).toHaveURL(/\/cases/); const mainContent = page.locator('main, [role="main"]'); await expect(mainContent).toBeVisible(); }); }); test.describe('后台内容管理功能测试', () => { test.beforeEach(async ({ page }) => { await page.goto(`${BASE_URL}/admin/login`); await page.waitForLoadState('networkidle'); const emailInput = page.locator('#email'); const passwordInput = page.locator('#password'); const submitButton = page.locator('button[type="submit"]'); await emailInput.fill(ADMIN_EMAIL); await passwordInput.fill(ADMIN_PASSWORD); await submitButton.click(); await page.waitForURL(/\/admin(?!\/login)/, { timeout: 15000 }); }); test('后台仪表盘加载', async ({ page }) => { await page.goto(`${BASE_URL}/admin`); await page.waitForLoadState('networkidle'); const heading = page.locator('h1, .text-2xl').first(); await expect(heading).toBeVisible(); console.log('后台仪表盘加载成功'); }); test('后台内容列表页面加载', async ({ page }) => { await page.goto(`${BASE_URL}/admin/content`); await page.waitForLoadState('networkidle'); const table = page.locator('table'); await expect(table).toBeVisible(); const rows = page.locator('tbody tr'); const count = await rows.count(); console.log(`后台内容列表数量: ${count}`); }); test('后台新建内容页面表单完整性', async ({ page }) => { await page.goto(`${BASE_URL}/admin/content/new`); await page.waitForLoadState('domcontentloaded'); await page.waitForSelector('input[placeholder="请输入标题"]', { timeout: 60000 }); const titleInput = page.locator('input[placeholder="请输入标题"]'); await expect(titleInput).toBeVisible(); const slugInput = page.locator('input[placeholder="url-slug"]'); await expect(slugInput).toBeVisible(); const typeSelect = page.locator('select').first(); await expect(typeSelect).toBeVisible(); const categoryInput = page.locator('input[placeholder="分类名称"]'); const hasCategory = await categoryInput.isVisible().catch(() => false); console.log(`分类输入框${hasCategory ? '存在' : '不存在'}`); const publishButton = page.locator('button:has-text("发布")'); await expect(publishButton).toBeVisible(); const saveDraftButton = page.locator('button:has-text("保存草稿"), button:has-text("保存")'); await expect(saveDraftButton).toBeVisible(); }); test('后台内容编辑页面加载', async ({ page }) => { await page.goto(`${BASE_URL}/admin/content`); await page.waitForLoadState('networkidle'); const rows = page.locator('tbody tr'); const count = await rows.count(); if (count > 0) { const firstEditLink = page.locator('tbody tr:first-child a[href*="/admin/content/"]').first(); const hasEditLink = await firstEditLink.isVisible().catch(() => false); if (hasEditLink) { await firstEditLink.click(); await page.waitForLoadState('domcontentloaded'); const titleInput = page.locator('input[placeholder="请输入标题"]'); await expect(titleInput).toBeVisible({ timeout: 30000 }); console.log('编辑页面加载成功'); } else { console.log('没有可编辑的内容'); } } else { console.log('内容列表为空'); } }); test('后台内容分类管理', async ({ page }) => { await page.goto(`${BASE_URL}/admin/categories`); await page.waitForLoadState('networkidle'); const heading = page.locator('h1, .text-2xl').first(); const hasHeading = await heading.isVisible().catch(() => false); console.log(`分类管理页面${hasHeading ? '可访问' : '不存在或无权限'}`); }); }); test.describe('内容导航和链接测试', () => { test('导航到不同内容类型页面', async ({ page }) => { const pages = [ { url: '/news', name: '新闻' }, { url: '/products', name: '产品' }, { url: '/services', name: '服务' }, { url: '/cases', name: '案例' }, ]; for (const p of pages) { await page.goto(`${BASE_URL}${p.url}`); await page.waitForLoadState('networkidle'); const url = page.url(); console.log(`${p.name}页面: ${url.includes(p.url) ? '可访问' : '不可访问'}`); } }); test('内容详情页访问', async ({ page }) => { await page.goto(`${BASE_URL}/news`); await page.waitForLoadState('networkidle'); const links = page.locator('a[href*="/news/"]'); const count = await links.count(); if (count > 0) { const firstLink = links.first(); const href = await firstLink.getAttribute('href'); if (href && !href.startsWith('http')) { await page.goto(`${BASE_URL}${href}`); await page.waitForLoadState('networkidle'); const mainContent = page.locator('main, article'); const isVisible = await mainContent.isVisible().catch(() => false); console.log(`详情页加载${isVisible ? '成功' : '失败'}`); } } else { console.log('没有可访问的新闻详情链接'); } }); }); test.describe('SEO和元数据测试', () => { test('页面标题验证', async ({ page }) => { const pages = [ { url: '/', name: '首页' }, { url: '/news', name: '新闻' }, { url: '/products', name: '产品' }, ]; for (const p of pages) { await page.goto(`${BASE_URL}${p.url}`); await page.waitForLoadState('networkidle'); const title = await page.title(); console.log(`${p.name}标题: ${title}`); expect(title.length).toBeGreaterThan(0); } }); test('Meta描述标签验证', async ({ page }) => { await page.goto(BASE_URL); await page.waitForLoadState('networkidle'); const metaDesc = page.locator('meta[name="description"]'); const hasMetaDesc = await metaDesc.count(); console.log(`Meta描述标签${hasMetaDesc > 0 ? '存在' : '不存在'}`); }); }); test.describe('响应式导航测试', () => { test('移动端导航菜单', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await page.goto(BASE_URL); await page.waitForLoadState('networkidle'); const menuButton = page.locator('button[aria-label*="菜单"], button[class*="menu"], button[class*="Menu"]'); const hasMenuButton = await menuButton.isVisible().catch(() => false); console.log(`移动端菜单按钮${hasMenuButton ? '存在' : '不存在'}`); if (hasMenuButton) { await menuButton.click(); await page.waitForSelector('nav, [class*="menu"], [class*="Menu"]', { state: 'visible', timeout: 5000 }); const navMenu = page.locator('nav, [class*="menu"], [class*="Menu"]'); const isVisible = await navMenu.isVisible().catch(() => false); console.log(`导航菜单${isVisible ? '展开' : '未展开'}`); } }); test('桌面端导航显示', async ({ page }) => { await page.setViewportSize({ width: 1920, height: 1080 }); await page.goto(BASE_URL); await page.waitForLoadState('networkidle'); const navLinks = page.locator('nav a'); const count = await navLinks.count(); console.log(`桌面端导航链接数量: ${count}`); expect(count).toBeGreaterThan(0); }); }); test.describe('页面加载性能测试', () => { test('各页面加载时间', async ({ page }) => { const pages = [ { url: '/', name: '首页' }, { url: '/news', name: '新闻' }, { url: '/products', name: '产品' }, { url: '/services', name: '服务' }, { url: '/cases', name: '案例' }, ]; for (const p of pages) { const startTime = Date.now(); await page.goto(`${BASE_URL}${p.url}`); await page.waitForLoadState('networkidle'); const loadTime = Date.now() - startTime; console.log(`${p.name}页面加载时间: ${loadTime}ms`); expect(loadTime).toBeLessThan(5000); } }); }); test.describe('错误处理测试', () => { test('访问不存在的页面', async ({ page }) => { await page.goto(`${BASE_URL}/nonexistent-page-12345`); await page.waitForLoadState('networkidle'); const errorElement = page.locator('[class*="error"], h1:has-text("404"), text=页面不存在'); const hasError = await errorElement.isVisible().catch(() => false); console.log(`404页面${hasError ? '正确显示' : '未显示'}`); }); test('后台访问无权限内容', async ({ browser }) => { const context = await browser.newContext(); const page = await context.newPage(); await page.goto(`${BASE_URL}/admin/content/99999`); await page.waitForLoadState('networkidle'); await page.waitForURL(/\/admin/, { timeout: 5000 }); const url = page.url(); console.log(`访问不存在内容后URL: ${url}`); await context.close(); }); }); test.describe('国际化支持测试', () => { test('页面语言属性', async ({ page }) => { await page.goto(BASE_URL); await page.waitForLoadState('networkidle'); const htmlLang = await page.locator('html').getAttribute('lang'); console.log(`页面语言: ${htmlLang || '未设置'}`); }); });