Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 92 additions & 6 deletions frontend/src/pages/projects/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import { MemoryRouter } from 'react-router'

import { AppRoutes } from '@/app'
import { characterApis } from '@/entities'
import { AuthenticatedAuthSession } from '@/test/auth-session'
import { createProjectAssetsBackend } from '@/test/project-assets-backend'

afterEach(() => {
cleanup()
vi.restoreAllMocks()
vi.unstubAllEnvs()
vi.unstubAllGlobals()
})
Expand All @@ -22,8 +24,8 @@ function installBackend() {

describe('ProjectsPage', () => {
it('renders backend Projects as the first browsing level', async () => {
installBackend()
const { container } = render(
const backend = installBackend()
render(
<AuthenticatedAuthSession>
<MemoryRouter initialEntries={['/projects']}>
<AppRoutes />
Expand All @@ -32,13 +34,51 @@ describe('ProjectsPage', () => {
)

expect(await screen.findByRole('heading', { name: '项目中心' })).toBeTruthy()
const createLink = await screen.findByRole('link', { name: '新建项目' })
const artwork = createLink.querySelector('img')
expect(artwork).toBeTruthy()
if (!artwork) throw new Error('新建项目入口缺少资产装饰图')
expect(artwork.getAttribute('src')).toContain('asset-library.png')
expect(artwork.getAttribute('aria-hidden')).toBe('true')
expect(screen.queryByText('新的资产空间')).toBeNull()
expect(screen.queryByText('按最近更新排列')).toBeNull()
expect(screen.getAllByRole('link', { name: '新建项目' })).toHaveLength(1)
expect(createLink.getAttribute('href')).toBe('/projects/new')
expect(await screen.findAllByRole('link', { name: /打开项目/ })).toHaveLength(2)
expect(screen.getByRole('link', { name: '打开项目 点灯人 · MVP' }).getAttribute('href')).toBe(
'/projects/42/assets',
const previewProject = screen.getByRole('link', { name: '打开项目 点灯人 · MVP' })
expect(previewProject.getAttribute('href')).toBe('/projects/42/assets')
expect(screen.getByRole('heading', { name: '最近项目 · 02' })).toBeTruthy()
expect(previewProject.querySelector('img')?.getAttribute('src')).toBe(
'https://cdn.windup.test/messenger-outfit.png',
)
expect(screen.getByText('低饱和像素绘本')).toBeTruthy()
expect(container.querySelectorAll('[data-project-card]')).toHaveLength(2)
const emptyProject = screen.getByRole('link', { name: '打开项目 空白海岸' })
expect(emptyProject.querySelector('img')).toBeNull()
expect(emptyProject.textContent).toContain('等待第一份角色资产')
expect(previewProject.textContent).toContain('08/04')
expect(screen.queryByText('项目名称')).toBeNull()
expect(screen.queryByText('视角 / 朝向')).toBeNull()
expect(screen.queryByRole('link', { name: /查看角色/ })).toBeNull()
expect(
backend.requests.every((request) =>
['/projects', '/characters'].includes(new URL(request.url).pathname),
),
).toBe(true)
expect(
backend.requests.filter((request) => new URL(request.url).pathname === '/projects'),
).toHaveLength(1)
const previewRequests = backend.requests.filter(
(request) => new URL(request.url).pathname === '/characters',
)
expect(previewRequests).toHaveLength(2)
expect(
previewRequests.map((request) => new URL(request.url).searchParams.get('project_id')),
).toEqual(['42', '99'])
expect(
previewRequests.every((request) => {
const query = new URL(request.url).searchParams
return query.get('page') === '1' && query.get('page_size') === '1'
}),
).toBe(true)
})

it('sends creation to the project create page and deletes through the Project API', async () => {
Expand Down Expand Up @@ -70,6 +110,52 @@ describe('ProjectsPage', () => {
).toBe(true)
})

it('falls back through character preview sources without blocking the gallery', async () => {
const backend = createProjectAssetsBackend({ projectCount: 3 })
vi.stubEnv('VITE_API_BASE_URL', 'https://api.windup.test')
vi.stubGlobal('fetch', backend.fetch)
const character = await characterApis.get('51')
vi.spyOn(characterApis, 'listByProject').mockImplementation(async (projectId) => {
if (Number(projectId) === 1002) throw new Error('preview unavailable')
return {
items: [
{
...character,
referenceImageUrl: Number(projectId) === 42 ? character.referenceImageUrl : null,
outfits: character.outfits.map((outfit) => ({ ...outfit, previewUrl: null })),
},
],
total: 1,
page: 1,
pageSize: 1,
}
})
render(
<AuthenticatedAuthSession>
<MemoryRouter initialEntries={['/projects']}>
<AppRoutes />
</MemoryRouter>
</AuthenticatedAuthSession>,
)

expect(await screen.findAllByRole('link', { name: /打开项目/ })).toHaveLength(3)
await waitFor(() => {
expect(
screen
.getByRole('link', { name: '打开项目 点灯人 · MVP' })
.querySelector('img')
?.getAttribute('src'),
).toBe('https://cdn.windup.test/messenger-reference.png')
expect(
screen
.getByRole('link', { name: '打开项目 空白海岸' })
.querySelector('img')
?.getAttribute('src'),
).toBe('https://cdn.windup.test/idle-01.png')
expect(screen.getByText('等待第一份角色资产')).toBeTruthy()
})
})

it('navigates every backend Project page instead of truncating after the first page', async () => {
const backend = createProjectAssetsBackend({ projectCount: 13 })
vi.stubEnv('VITE_API_BASE_URL', 'https://api.windup.test')
Expand Down
Loading
Loading