221 lines
6.9 KiB
TypeScript
221 lines
6.9 KiB
TypeScript
/** @jest-environment node */
|
|
|
|
import type { Session } from 'next-auth'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { getPerformanceSurveyContext } from '@/lib/performance-survey-service'
|
|
import { getFirstPassReviewProjects } from '@/lib/work-review-service'
|
|
|
|
jest.mock('@/lib/prisma', () => ({
|
|
prisma: {
|
|
workReviewVersion: { groupBy: jest.fn() },
|
|
workReviewProject: { count: jest.fn(), findMany: jest.fn() },
|
|
},
|
|
}))
|
|
|
|
jest.mock('@/lib/performance-survey-service', () => ({
|
|
getPerformanceSurveyContext: jest.fn(),
|
|
}))
|
|
|
|
jest.mock('@/lib/performance-survey-report-sync', () => ({
|
|
getPerformanceSurveyAccountProfileWhere: jest.fn(async () => ({})),
|
|
}))
|
|
|
|
jest.mock('@/lib/work-review-notifications', () => ({
|
|
ensureWorkReviewNotificationWorkerStarted: jest.fn(),
|
|
queueWorkReviewNotification: jest.fn(async () => undefined),
|
|
}))
|
|
|
|
const session = { user: { id: 'manager-user' } } as Session
|
|
|
|
describe('department dashboard first-pass review details', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
;(getPerformanceSurveyContext as jest.Mock).mockResolvedValue({
|
|
user: { id: 'manager-user', username: 'manager' },
|
|
employee: { id: 'manager-employee', displayName: '主管' },
|
|
scopeDepartmentPath: '江苏简尚品牌管理有限公司/品牌中心',
|
|
isSystemAdmin: true,
|
|
isDepartmentAdmin: true,
|
|
isLeadership: true,
|
|
})
|
|
;(prisma.workReviewVersion.groupBy as jest.Mock).mockResolvedValue([
|
|
{ status: 'approved', _count: { _all: 2 } },
|
|
{ status: 'pending_review', _count: { _all: 1 } },
|
|
])
|
|
;(prisma.workReviewProject.count as jest.Mock).mockImplementation(({ where }) => (
|
|
JSON.stringify(where).includes('"status":"approved"') ? Promise.resolve(5) : Promise.resolve(1)
|
|
))
|
|
;(prisma.workReviewProject.findMany as jest.Mock)
|
|
.mockResolvedValueOnce([{
|
|
id: 'project-1',
|
|
title: '品牌主视觉',
|
|
brand: null,
|
|
description: '第一版视觉作品',
|
|
status: 'revision_required',
|
|
latestVersion: 2,
|
|
submitterName: '提交人',
|
|
managerName: '主管',
|
|
finalReviewerName: '元泽',
|
|
workDate: new Date('2026-08-28T00:00:00.000Z'),
|
|
createdAt: new Date('2026-08-28T01:00:00.000Z'),
|
|
updatedAt: new Date('2026-08-30T01:00:00.000Z'),
|
|
versions: [{
|
|
status: 'approved',
|
|
submittedAt: new Date('2026-08-28T01:00:00.000Z'),
|
|
completedAt: new Date('2026-08-28T03:00:00.000Z'),
|
|
reviews: [{
|
|
reviewerRole: 'manager',
|
|
reviewerName: '主管',
|
|
decision: 'approved',
|
|
reviewedAt: new Date('2026-08-28T02:00:00.000Z'),
|
|
}],
|
|
}],
|
|
}])
|
|
.mockResolvedValueOnce([
|
|
{ submitterName: '林二', status: 'approved', versions: [{ status: 'revision_required' }] },
|
|
{ submitterName: '林一', status: 'approved', versions: [{ status: 'approved' }] },
|
|
{ submitterName: '林一', status: 'approved', versions: [] },
|
|
{ submitterName: '提交人', status: 'revision_required', versions: [{ status: 'approved' }] },
|
|
])
|
|
})
|
|
|
|
test('summary keeps the full brand-center V1 population while search narrows only detail rows', async () => {
|
|
const result = await getFirstPassReviewProjects(session, {
|
|
page: 2,
|
|
pageSize: 10,
|
|
query: '主视觉',
|
|
})
|
|
|
|
expect(prisma.workReviewVersion.groupBy).toHaveBeenCalledWith({
|
|
by: ['status'],
|
|
where: {
|
|
version: 1,
|
|
project: { is: {} },
|
|
},
|
|
_count: { _all: true },
|
|
})
|
|
expect(prisma.workReviewProject.count).toHaveBeenCalledWith({
|
|
where: {
|
|
AND: [
|
|
{},
|
|
{ status: 'approved' },
|
|
],
|
|
},
|
|
})
|
|
expect(prisma.workReviewProject.count).toHaveBeenCalledWith({
|
|
where: expect.objectContaining({
|
|
AND: expect.arrayContaining([
|
|
expect.objectContaining({ AND: expect.arrayContaining([
|
|
{},
|
|
{ versions: { some: { version: 1 } } },
|
|
]) }),
|
|
expect.objectContaining({ OR: expect.any(Array) }),
|
|
]),
|
|
}),
|
|
})
|
|
expect(prisma.workReviewProject.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
|
select: expect.objectContaining({
|
|
id: true,
|
|
title: true,
|
|
description: true,
|
|
createdAt: true,
|
|
versions: {
|
|
where: { version: 1 },
|
|
select: {
|
|
status: true,
|
|
submittedAt: true,
|
|
completedAt: true,
|
|
reviews: {
|
|
select: {
|
|
reviewerRole: true,
|
|
reviewerName: true,
|
|
decision: true,
|
|
reviewedAt: true,
|
|
},
|
|
orderBy: [{ reviewedAt: 'asc' }, { reviewerRole: 'asc' }],
|
|
},
|
|
},
|
|
take: 1,
|
|
},
|
|
}),
|
|
orderBy: [{ updatedAt: 'desc' }, { workDate: 'desc' }, { id: 'desc' }],
|
|
skip: 10,
|
|
take: 10,
|
|
}))
|
|
expect(prisma.workReviewProject.findMany).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
select: expect.objectContaining({ imageUrls: true }),
|
|
}))
|
|
expect(prisma.workReviewProject.findMany).toHaveBeenNthCalledWith(2, {
|
|
where: {
|
|
AND: [
|
|
{},
|
|
{ versions: { some: { version: 1 } } },
|
|
],
|
|
},
|
|
select: {
|
|
submitterName: true,
|
|
status: true,
|
|
versions: {
|
|
where: { version: 1 },
|
|
select: { status: true },
|
|
take: 1,
|
|
},
|
|
},
|
|
})
|
|
|
|
expect(result.summary).toMatchObject({
|
|
totalUploads: 3,
|
|
totalApproved: 5,
|
|
firstPassApproved: 2,
|
|
rate: 40,
|
|
})
|
|
expect(result.submitterRates).toEqual([
|
|
{
|
|
submitterName: '林一',
|
|
totalUploads: 1,
|
|
totalApproved: 2,
|
|
firstPassApproved: 1,
|
|
rate: 50,
|
|
},
|
|
{
|
|
submitterName: '林二',
|
|
totalUploads: 1,
|
|
totalApproved: 1,
|
|
firstPassApproved: 0,
|
|
rate: 0,
|
|
},
|
|
{
|
|
submitterName: '提交人',
|
|
totalUploads: 1,
|
|
totalApproved: 0,
|
|
firstPassApproved: 1,
|
|
rate: null,
|
|
},
|
|
])
|
|
expect(result.items).toEqual([{
|
|
id: 'project-1',
|
|
title: '品牌主视觉',
|
|
brand: null,
|
|
description: '第一版视觉作品',
|
|
currentStatus: 'revision_required',
|
|
latestVersion: 2,
|
|
submitterName: '提交人',
|
|
managerName: '主管',
|
|
finalReviewerName: '元泽',
|
|
workDate: '2026-08-28T00:00:00.000Z',
|
|
createdAt: '2026-08-28T01:00:00.000Z',
|
|
updatedAt: '2026-08-30T01:00:00.000Z',
|
|
firstVersionStatus: 'approved',
|
|
firstVersionSubmittedAt: '2026-08-28T01:00:00.000Z',
|
|
firstVersionCompletedAt: '2026-08-28T03:00:00.000Z',
|
|
firstVersionReviews: [{
|
|
reviewerRole: 'manager',
|
|
reviewerName: '主管',
|
|
decision: 'approved',
|
|
reviewedAt: '2026-08-28T02:00:00.000Z',
|
|
}],
|
|
}])
|
|
expect(result.pagination).toEqual({ page: 2, pageSize: 10, total: 1, totalPages: 1 })
|
|
})
|
|
})
|