// @vitest-environment jsdom import { CFB, read, utils } from 'xlsx'; import { describe, expect, it } from 'vitest'; import { buildResultsWindowExcel } from '../../src/exports/results-window-excel'; const content = { title: 'Affected synthetic servers', text: 'Source prose', sourceSections: [{ title: 'Evidence', value: 'Original source' }], tables: [ { id: 'first', title: '1 Domain controllers', rows: [['Host', 'Value'], ['DUMMY-DC', '=literal']] }, { id: 'second', title: '2 Accounts', rows: [['Account'], ['000123']] } ] }; describe.each([undefined, new Set(['second'])])('Results Excel title and order (%s)', selected => { it('starts with actual Results and writes a larger bold white-on-black merged title above column headers', () => { const bytes = buildResultsWindowExcel(content, selected); const workbook = read(bytes, { type: 'array', cellStyles: true }); const expected = selected ? ['Results', 'Export info'] : ['Results', 'Results 02', 'Source text', 'Export info']; expect(workbook.SheetNames).toEqual(expected); const archive = CFB.read(new Uint8Array(bytes), { type: 'array' }); const xml = (path: string) => new DOMParser().parseFromString(new TextDecoder().decode(new Uint8Array(CFB.find(archive, `/${path}`).content)), 'application/xml'); const styles = xml('xl/styles.xml'); expect(styles.querySelector('parsererror')).toBeNull(); for (let index = 0; index < workbook.SheetNames.length; index++) { const name = workbook.SheetNames[index]; const sheet = workbook.Sheets[name]; const title = sheet.A1.v as string; const table = (selected ? content.tables.slice(1) : content.tables)[index]; const isTable = index < (selected ? 1 : 2); expect(title).toBe(isTable ? `${content.title}\n${table.title}` : content.title); if (isTable) expect(utils.sheet_to_json(sheet, { header: 1, defval: '' }).slice(1, 1 + table.rows.length)).toEqual(table.rows); expect(sheet['!autofilter']!.ref).toMatch(/^A2:/); const part = xml(`xl/worksheets/sheet${index + 1}.xml`); const cell = part.querySelector('c[r="A1"]')!; const xf = styles.querySelector('cellXfs')!.children[Number(cell.getAttribute('s'))]; const font = styles.querySelector('fonts')!.children[Number(xf.getAttribute('fontId'))]; expect(font.querySelector('b')).not.toBeNull(); expect(font.querySelector('sz')!.getAttribute('val')).toBe('16'); expect(font.querySelector('color')!.getAttribute('rgb')).toBe('FFFFFFFF'); const fill = styles.querySelector('fills')!.children[Number(xf.getAttribute('fillId'))]; expect(fill.querySelector('patternFill')!.getAttribute('patternType')).toBe('solid'); expect(fill.querySelector('fgColor')!.getAttribute('rgb')).toBe('FF000000'); expect(xf.querySelector('alignment')!.getAttribute('wrapText')).toBe('1'); expect(Number(part.querySelector('row[r="1"]')!.getAttribute('ht'))).toBeGreaterThanOrEqual(48); if (isTable && table.rows[0].length > 1) expect(sheet['!merges']).toEqual([{ s: { r: 0, c: 0 }, e: { r: 0, c: 1 } }]); } if (!selected) { expect(workbook.Sheets.Results.B3.v).toBe('=literal'); expect(workbook.Sheets.Results.B3.f).toBeUndefined(); expect(workbook.Sheets.Results.A6.v).toBe(content.text); expect(workbook.Sheets.Results['!autofilter']?.ref).toBe('A2:B3'); } }); }); it('keeps a text-only Result first and gives its complete long finding title enough heading height', () => { const title = 'Long synthetic finding '.repeat(12); const bytes = buildResultsWindowExcel({ title, text: 'Entire Result text', tables: [], sourceSections: [] }); const workbook = read(bytes, { type: 'array', cellStyles: true }); expect(workbook.SheetNames).toEqual(['Results', 'Export info']); expect(workbook.Sheets.Results.A1.v).toBe(title); expect(workbook.Sheets.Results.A3.v).toBe('Entire Result text'); const archive = CFB.read(new Uint8Array(bytes), { type: 'array' }); const part = new DOMParser().parseFromString(new TextDecoder().decode(new Uint8Array(CFB.find(archive, '/xl/worksheets/sheet1.xml').content)), 'application/xml'); expect(part.querySelector('c[r="A1"]')!.hasAttribute('s')).toBe(true); expect(Number(part.querySelector('row[r="1"]')!.getAttribute('ht'))).toBeGreaterThan(48); }); it('writes the original styled finding heading on every auxiliary sheet including long-text parts', () => { const bytes = buildResultsWindowExcel({ ...content, findingContext: [{section:'Description',label:'Description',value:'Complete description '.repeat(2000)}] }); const workbook = read(bytes, {type:'array'}); expect(workbook.SheetNames).toEqual(['Results','Results 02','Source text','Finding context','Export info','Long text']); const archive = CFB.read(new Uint8Array(bytes), {type:'array'}); const xml = (path:string) => new DOMParser().parseFromString(new TextDecoder().decode(new Uint8Array(CFB.find(archive, `/${path}`).content)), 'application/xml'); const styles = xml('xl/styles.xml'); workbook.SheetNames.forEach((name,index) => { expect(workbook.Sheets[name].A1.v).toContain(content.title); const part=xml(`xl/worksheets/sheet${index+1}.xml`); const xf=styles.querySelector('cellXfs')!.children[Number(part.querySelector('c[r="A1"]')!.getAttribute('s'))]; const font=styles.querySelector('fonts')!.children[Number(xf.getAttribute('fontId'))]; const fill=styles.querySelector('fills')!.children[Number(xf.getAttribute('fillId'))]; expect(font.querySelector('b')).not.toBeNull(); expect(font.querySelector('color')!.getAttribute('rgb')).toBe('FFFFFFFF'); expect(fill.querySelector('fgColor')!.getAttribute('rgb')).toBe('FF000000'); }); });