import ExcelJS from 'exceljs' import { buildPerformanceSurveyDashboardWorkbook, buildPerformanceSurveyExportTable, choosePerformanceSurveyExportDestination, sanitizePerformanceSurveyExportFileName, savePerformanceSurveyDashboardWorkbook, type PerformanceSurveyDashboardExportData, } from '@/lib/performance-survey-dashboard-export' const exportData: PerformanceSurveyDashboardExportData = { month: '2026-07', filters: { positionGroupCode: 'brand_creative_manager' }, selectedPositionGroup: { code: 'brand_creative_manager', name: '品牌创意主管' }, positionGroups: [{ code: 'brand_creative_manager', name: '品牌创意主管' }], templates: [ { code: 'visual_review', name: '品牌调性视觉评分', maxTotal: 120 }, { code: 'coordination', name: '跨部门协同', maxTotal: 120 }, { code: 'cooperation', name: '工作配合度', maxTotal: 120 }, ], public: { rows: [ { month: '2026-07', employee: { displayName: '伯言', jobNumber: 'JS15040602', departmentName: '品牌创意A部', positionLevel: 'M2', }, cells: { visual_review: { score: null, surveyCount: 1, positionGroupCode: 'brand_creative_manager' }, coordination: { score: 114.5, surveyCount: 1, positionGroupCode: 'brand_creative_manager' }, cooperation: { score: 98, surveyCount: 1, positionGroupCode: 'brand_creative_manager' }, }, }, ], }, } describe('performance survey dashboard xlsx export', () => { test('keeps numeric scores numeric and marks pending assessments clearly', () => { const table = buildPerformanceSurveyExportTable(exportData) expect(table.headers).toEqual([ '序号', '岗位', '考核月份', '姓名', '工号', '组织', '职级', '品牌调性视觉评分(满分120)', '跨部门协同(满分120)', '工作配合度(满分120)', ]) expect(table.rows[0]).toEqual([ 1, '品牌创意主管', '2026-07', '伯言', 'JS15040602', '品牌创意A部', 'M2', '待评分', 114.5, 98, ]) }) test('creates a WPS-compatible workbook with title, filters and score rows', async () => { const workbook = await buildPerformanceSurveyDashboardWorkbook(exportData) const buffer = await workbook.xlsx.writeBuffer() const reloaded = new ExcelJS.Workbook() await reloaded.xlsx.load(buffer) const worksheet = reloaded.worksheets[0] expect(worksheet.name).toBe('2026-07_品牌创意主管') expect(worksheet.getCell('A1').value).toBe('2026-07 · 品牌创意主管考核调研得分') expect(worksheet.getCell('A5').value).toBe(1) expect(worksheet.getCell('H5').value).toBe('待评分') expect(worksheet.getCell('I5').value).toBe(114.5) expect(worksheet.autoFilter).toBe('A4:J4') }) test('sanitizes file-system reserved characters', () => { expect(sanitizePerformanceSurveyExportFileName('考核:品牌/设计*主管')) .toBe('考核_品牌_设计_主管.xlsx') }) test('keeps the browser context when opening the native save picker', async () => { const handle = { createWritable: jest.fn() } const picker = jest.fn(function (this: unknown) { expect(this).toBe(window) return Promise.resolve(handle) }) Object.defineProperty(window, 'showSaveFilePicker', { configurable: true, value: picker, }) await expect(choosePerformanceSurveyExportDestination('考核调研.xlsx')) .resolves.toEqual({ kind: 'file-system', handle }) expect(picker).toHaveBeenCalledWith(expect.objectContaining({ suggestedName: '考核调研.xlsx', })) delete (window as typeof window & { showSaveFilePicker?: unknown }).showSaveFilePicker }) test('uses a native blob download when the save picker is unavailable', async () => { const createObjectURL = jest.fn(() => 'blob:performance-survey-export') const revokeObjectURL = jest.fn() Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: createObjectURL }) Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, value: revokeObjectURL }) const click = jest.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {}) await savePerformanceSurveyDashboardWorkbook( exportData, '考核调研_品牌创意主管_2026-07.xlsx', { kind: 'download' }, ) expect(createObjectURL).toHaveBeenCalledWith(expect.any(Blob)) expect(click).toHaveBeenCalledTimes(1) await new Promise((resolve) => window.setTimeout(resolve, 0)) expect(revokeObjectURL).toHaveBeenCalledWith('blob:performance-survey-export') click.mockRestore() }) })