fix: FilePreview fileType case + Tailwind v4 gradient transparent bug
- FilePreview.vue: add normalizedFileType computed to handle backend returning uppercase HTML/MD/PPTX (fixes preview/download buttons) - FilePreview.vue: bg-gradient-to-r from-orange-500 -> bg-orange-500 (Tailwind v4 gradient + CSS variable = transparent) - ReportCard.vue: bg-gradient-to-r -> bg-orange-600 for selected state - Add .opencode/, node_modules/, dist/ to .gitignore - Initial git setup for publish project
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Cover Image Upload E2E Tests
|
||||
* Tests for the cover image upload functionality on ProjectDetail page
|
||||
*/
|
||||
test.describe('Cover Image Upload', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
test('should navigate to project detail page', async ({ page }) => {
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
// Click the first project card by text
|
||||
await page.locator('text=项目一').first().click()
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should enter project edit mode by clicking project name', async ({ page }) => {
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
await page.locator('text=项目一').first().click()
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Click on project name to enter edit mode
|
||||
const projectName = page.locator('h2').first()
|
||||
await expect(projectName).toBeVisible({ timeout: 5000 })
|
||||
await projectName.click()
|
||||
|
||||
// Should show edit form with file input
|
||||
await expect(page.locator('input[type="file"]').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should show save and cancel buttons in edit mode', async ({ page }) => {
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
await page.locator('text=项目一').first().click()
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Enter edit mode
|
||||
const projectName = page.locator('h2').first()
|
||||
await projectName.click()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
// Should show save button
|
||||
const saveButton = page.locator('button:has-text("保存")')
|
||||
await expect(saveButton).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Should show cancel button
|
||||
const cancelButton = page.locator('button:has-text("取消")')
|
||||
await expect(cancelButton).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should have image file input accepting image types', async ({ page }) => {
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
await page.locator('text=项目一').first().click()
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Enter edit mode
|
||||
const projectName = page.locator('h2').first()
|
||||
await projectName.click()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
// File input should accept images
|
||||
const fileInput = page.locator('input[type="file"]').first()
|
||||
await expect(fileInput).toBeVisible({ timeout: 5000 })
|
||||
const accept = await fileInput.getAttribute('accept')
|
||||
expect(accept).toBe('image/*')
|
||||
})
|
||||
|
||||
test('should cancel edit mode and restore original state', async ({ page }) => {
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
await page.locator('text=项目一').first().click()
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Enter edit mode
|
||||
const projectName = page.locator('h2').first()
|
||||
await projectName.click()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
// Click cancel
|
||||
const cancelButton = page.locator('button:has-text("取消")')
|
||||
await cancelButton.click()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
// Edit form should be hidden, back to display mode
|
||||
const fileInput = page.locator('input[type="file"]')
|
||||
await expect(fileInput).toHaveCount(0, { timeout: 5000 })
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Cover Image Refresh E2E Tests
|
||||
* Tests that cover image changes reflect on project list after saving
|
||||
*/
|
||||
test.describe('Cover Image - List Refresh', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
test('should refresh project list after navigating back from detail', async ({ page }) => {
|
||||
// Get initial project count
|
||||
await expect(page.locator('text=个项目').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Navigate to a project
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
await page.locator('text=项目一').first().click()
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Navigate back to project list
|
||||
await page.locator('a[href="/"]').click()
|
||||
await expect(page.locator('text=选择项目').first()).toBeVisible({ timeout: 10000 })
|
||||
|
||||
// Should still show correct project count (list refreshed)
|
||||
await expect(page.locator('text=个项目').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display report count on stats cards', async ({ page }) => {
|
||||
// Stats cards should show actual counts
|
||||
await expect(page.locator('text=个项目').first()).toBeVisible({ timeout: 15000 })
|
||||
await expect(page.locator('text=份报告').first()).toBeVisible({ timeout: 5000 })
|
||||
await expect(page.locator('text=文件类型').first()).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Check that the stats numbers are visible (4xl font size)
|
||||
const statsNumbers = page.locator('.text-4xl')
|
||||
const count = await statsNumbers.count()
|
||||
expect(count).toBeGreaterThanOrEqual(3)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,141 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Project Management E2E Tests
|
||||
* Tests for the UI redesigned project list and detail pages
|
||||
*/
|
||||
test.describe('Project Management (New UI)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/')
|
||||
// Wait for the page to fully load
|
||||
await page.waitForLoadState('networkidle')
|
||||
// Wait for content to render
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
test('should display project list with mock data', async ({ page }) => {
|
||||
// Verify that projects are displayed
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
await expect(page.locator('text=项目二').first()).toBeVisible({ timeout: 5000 })
|
||||
await expect(page.locator('text=项目三').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should navigate to project detail page when clicking a project card', async ({ page }) => {
|
||||
// Wait for project to be visible
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on the project card (large card with background image)
|
||||
// The new UI uses a carousel with ProjectCard components
|
||||
const projectCards = page.locator('.group.relative.h-\\[420px\\]')
|
||||
await projectCards.first().click()
|
||||
|
||||
// Verify navigation to project detail
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should display stats cards with correct counts', async ({ page }) => {
|
||||
// Wait for projects to load
|
||||
await expect(page.locator('text=个项目').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Check stats cards are visible
|
||||
await expect(page.locator('text=个项目').first()).toBeVisible({ timeout: 5000 })
|
||||
await expect(page.locator('text=份报告').first()).toBeVisible({ timeout: 5000 })
|
||||
await expect(page.locator('text=文件类型').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display project carousel with navigation arrows', async ({ page }) => {
|
||||
// Wait for projects to load
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Check carousel navigation arrows exist
|
||||
const leftArrow = page.locator('button').filter({ has: page.locator('svg path[d*="M15 19l-7-7 7-7"]') })
|
||||
const rightArrow = page.locator('button').filter({ has: page.locator('svg path[d*="M9 5l7 7-7 7"]') })
|
||||
|
||||
// At least one navigation button should be visible
|
||||
await expect(page.locator('button').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display project cards with report count badges', async ({ page }) => {
|
||||
// Check that project cards show report count
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
// Report count badge should be visible in cards
|
||||
await expect(page.locator('text=份报告').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should navigate to project detail and show reports', async ({ page }) => {
|
||||
// Navigate to a project
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on a project card
|
||||
const projectCards = page.locator('.group.relative.h-\\[420px\\]')
|
||||
await projectCards.first().click()
|
||||
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Should show the project name in detail page
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should navigate back to project list from project detail', async ({ page }) => {
|
||||
// Navigate to a project
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
const projectCards = page.locator('.group.relative.h-\\[420px\\]')
|
||||
await projectCards.first().click()
|
||||
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Click back button (router-link to="/")
|
||||
await page.locator('a[href="/"]').click()
|
||||
|
||||
// Verify we're back on the project list
|
||||
await expect(page.locator('text=选择项目').first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should display glass effect sidebar in project detail', async ({ page }) => {
|
||||
// Navigate to a project
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
const projectCards = page.locator('.group.relative.h-\\[420px\\]')
|
||||
await projectCards.first().click()
|
||||
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Check that the sidebar has glass effect class
|
||||
const sidebar = page.locator('.glass-light')
|
||||
await expect(sidebar).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display report cards in project detail sidebar', async ({ page }) => {
|
||||
// Navigate to a project
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
const projectCards = page.locator('.group.relative.h-\\[420px\\]')
|
||||
await projectCards.first().click()
|
||||
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Wait for reports to load (look for report file type badges)
|
||||
await expect(page.locator('text=HTML').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should show loading state while fetching', async ({ page }) => {
|
||||
// The loading spinner should appear briefly
|
||||
// But we can verify the content eventually loads
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
})
|
||||
|
||||
test('should display correct report count from backend', async ({ page }) => {
|
||||
// Wait for projects to load
|
||||
await expect(page.locator('text=个项目').first()).toBeVisible({ timeout: 15000 })
|
||||
// Stats cards show actual report counts from backend
|
||||
await expect(page.locator('.text-4xl').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display cover image on project cards when available', async ({ page }) => {
|
||||
// Wait for project cards to load
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
// ProjectCard should render with background image style when coverImage is set
|
||||
// Cards with cover image will have background-image CSS property
|
||||
const projectCards = page.locator('.group.relative.h-\\[420px\\]')
|
||||
const cardCount = await projectCards.count()
|
||||
expect(cardCount).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,121 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Report View E2E Tests (New UI)
|
||||
* Tests report viewing, rendering, and download functionality
|
||||
*/
|
||||
test.describe('Report View (New UI)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/project/1')
|
||||
await page.waitForLoadState('networkidle')
|
||||
// Wait for content to render
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
test('should render HTML content in iframe preview', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on HTML report
|
||||
await page.click('text=2026-05-22 日报.html')
|
||||
|
||||
// Wait for iframe to be visible
|
||||
const iframe = page.locator('iframe[srcdoc]')
|
||||
await expect(iframe).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should render Markdown content with proper formatting', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on MD report
|
||||
await page.click('text=2026-05-21 日报.md')
|
||||
|
||||
// Verify markdown is rendered (not raw markdown)
|
||||
// The header should be rendered as h1
|
||||
const header = page.locator('h1:has-text("日报标题")')
|
||||
await expect(header).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should show download button for PPTX reports', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on PPTX report
|
||||
await page.click('text=2026-05-20 周报.pptx')
|
||||
|
||||
// Verify download button is visible (new UI has single download button for all types)
|
||||
const downloadBtn = page.locator('button:has-text("下载")')
|
||||
await expect(downloadBtn).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should show report name in preview header when report is selected', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on HTML report
|
||||
await page.click('text=2026-05-22 日报.html')
|
||||
|
||||
// Verify report name appears in preview header
|
||||
await expect(page.locator('h3:has-text("2026-05-22 日报.html")').first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should show report date and size in preview header', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on HTML report
|
||||
await page.click('text=2026-05-22 日报.html')
|
||||
|
||||
// Verify date and size appear
|
||||
await expect(page.locator('text=2026-05-22').first()).toBeVisible({ timeout: 5000 })
|
||||
await expect(page.locator('text=15KB').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should have download button for all report types', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on HTML report
|
||||
await page.click('text=2026-05-22 日报.html')
|
||||
await expect(page.locator('button:has-text("下载")').first()).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Click on MD report
|
||||
await page.click('text=2026-05-21 日报.md')
|
||||
await expect(page.locator('button:has-text("下载")').first()).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Click on PPTX report
|
||||
await page.click('text=2026-05-20 周报.pptx')
|
||||
await expect(page.locator('button:has-text("下载")').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should navigate between reports smoothly', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Select first report
|
||||
await page.click('text=2026-05-22 日报.html')
|
||||
await expect(page.locator('h3:has-text("2026-05-22 日报.html")').first()).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Select second report
|
||||
await page.click('text=2026-05-21 日报.md')
|
||||
await expect(page.locator('h3:has-text("2026-05-21 日报.md")').first()).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Select third report
|
||||
await page.click('text=2026-05-20 周报.pptx')
|
||||
await expect(page.locator('h3:has-text("2026-05-20 周报.pptx")').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should show empty state for non-existent project', async ({ page }) => {
|
||||
// Navigate to project with no reports (project ID 999 - mock data returns empty)
|
||||
await page.goto('/project/999')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Should show the project detail page with "项目 999" header
|
||||
await expect(page.locator('h2:has-text("项目 999")')).toBeVisible({ timeout: 15000 })
|
||||
// With no reports, should show empty state in list
|
||||
await expect(page.locator('text=暂无报告').first()).toBeVisible({ timeout: 5000 })
|
||||
// Should show empty state in preview
|
||||
await expect(page.locator('text=选择一份报告以预览')).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,100 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Report Upload E2E Tests (New UI)
|
||||
* Tests report upload functionality in the project detail view
|
||||
*/
|
||||
test.describe('Report Upload (New UI)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate directly to a project detail page
|
||||
await page.goto('/project/1')
|
||||
await page.waitForLoadState('networkidle')
|
||||
// Wait for content to render
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
test('should display existing reports in the report list', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Verify HTML report appears in list
|
||||
await expect(page.locator('text=2026-05-22 日报.html')).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Verify MD report appears in list
|
||||
await expect(page.locator('text=2026-05-21 日报.md')).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Verify PPTX report appears in list
|
||||
await expect(page.locator('text=2026-05-20 周报.pptx')).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display report type badges in sidebar', async ({ page }) => {
|
||||
// Check that file type badges are visible
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// File type labels should be visible
|
||||
await expect(page.locator('text=HTML').first()).toBeVisible({ timeout: 5000 })
|
||||
await expect(page.locator('text=Markdown').first()).toBeVisible({ timeout: 5000 })
|
||||
await expect(page.locator('text=PowerPoint').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should select and preview HTML report', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on HTML report
|
||||
await page.click('text=2026-05-22 日报.html')
|
||||
|
||||
// Verify preview appears (HTML reports show in iframe)
|
||||
await expect(page.locator('iframe[srcdoc]')).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should select and preview MD report', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on MD report
|
||||
await page.click('text=2026-05-21 日报.md')
|
||||
|
||||
// Verify markdown content is rendered
|
||||
await expect(page.locator('text=日报标题')).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.locator('text=工作内容')).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should show download button for PPTX report', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on PPTX report
|
||||
await page.click('text=2026-05-20 周报.pptx')
|
||||
|
||||
// Verify download UI appears (new UI has unified download button)
|
||||
await expect(page.locator('button:has-text("下载")').first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should show empty state when no report is selected', async ({ page }) => {
|
||||
// The page starts without a selected report
|
||||
// Empty state should be visible (select prompt)
|
||||
await expect(page.locator('text=选择一份报告以预览')).toBeVisible({ timeout: 15000 })
|
||||
})
|
||||
|
||||
test('should highlight selected report in the list', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Click on a report
|
||||
await page.click('text=2026-05-22 日报.html')
|
||||
|
||||
// The selected report should have different styling (we can't easily test color, but we can test it's clickable)
|
||||
// Report should remain selected
|
||||
await expect(page.locator('iframe[srcdoc]')).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should show glass effect sidebar', async ({ page }) => {
|
||||
// Wait for project to load
|
||||
await expect(page.locator('h2').first()).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Check that the sidebar has glass effect class
|
||||
const sidebar = page.locator('.glass-light')
|
||||
await expect(sidebar).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,119 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Responsive Layout E2E Tests
|
||||
* Tests responsive design for PC (1920px) and Mobile (375px) viewports
|
||||
* Note: Sidebar (<aside>) exists only on ProjectDetail.vue, not ProjectList.vue
|
||||
*/
|
||||
test.describe('Responsive Layout', () => {
|
||||
test('should display main content on PC width (1920px)', async ({ page }) => {
|
||||
// Set PC viewport
|
||||
await page.setViewportSize({ width: 1920, height: 1080 })
|
||||
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Main content should be visible
|
||||
const mainContent = page.locator('main')
|
||||
await expect(mainContent).toBeVisible({ timeout: 15000 })
|
||||
|
||||
// Page title should be visible
|
||||
await expect(page.locator('text=选择项目')).toBeVisible({ timeout: 5000 })
|
||||
|
||||
// Projects should be displayed
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display stats cards on mobile width (375px)', async ({ page }) => {
|
||||
// Set mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Main content should be visible
|
||||
const mainContent = page.locator('main')
|
||||
await expect(mainContent).toBeVisible({ timeout: 10000 })
|
||||
|
||||
// Stats cards should be visible
|
||||
await expect(page.locator('text=个项目')).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should display project cards in single column on mobile', async ({ page }) => {
|
||||
// Set mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Project cards should still be visible
|
||||
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
|
||||
})
|
||||
|
||||
test('should navigate to project detail on mobile', async ({ page }) => {
|
||||
// Set mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Click on a project to navigate to detail (use force to bypass overlay)
|
||||
await page.locator('text=项目一').first().click({ force: true })
|
||||
|
||||
// Should navigate to project detail
|
||||
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should display sidebar in project detail on PC width', async ({ page }) => {
|
||||
// Set PC viewport
|
||||
await page.setViewportSize({ width: 1920, height: 1080 })
|
||||
|
||||
// First go to home page to load mock data
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Navigate to project detail via click
|
||||
await page.locator('text=项目一').first().click({ force: true })
|
||||
await page.waitForURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Wait for the sidebar to appear
|
||||
await expect(page.locator('text=返回项目列表')).toBeVisible({ timeout: 15000 })
|
||||
})
|
||||
|
||||
test('should display reports list in project detail on mobile width', async ({ page }) => {
|
||||
// Set mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
// First go to home page to load mock data
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Navigate to project detail via click
|
||||
await page.locator('text=项目一').first().click({ force: true })
|
||||
await page.waitForURL(/\/project\/\d+/, { timeout: 10000 })
|
||||
|
||||
// Reports should be visible (from mock data)
|
||||
await expect(page.locator('text=2026-05-22 日报.html')).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should close sidebar after navigation on mobile', async ({ page }) => {
|
||||
// Set mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
await page.goto('/project/1')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Navigate back to project list
|
||||
await page.locator('text=返回项目列表').click()
|
||||
|
||||
// Should be back on project list page
|
||||
await expect(page).toHaveURL(/\/$/, { timeout: 10000 })
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user