Assessor_2.0/tests/exports/results-excel-format.test.ts

78 lines
5.6 KiB
TypeScript

// @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');
});
});