// @vitest-environment jsdom import { act } from 'react'; import { createRoot } from 'react-dom/client'; import { afterEach, expect, it, vi } from 'vitest'; import { read, utils } from 'xlsx'; import { FloatingResultsWindow } from '../../src/features/linked-results/FloatingResultsWindow'; import { resultsFindingContext } from '../../src/features/linked-results/results-excel-content'; import { buildResultsWindowExcel } from '../../src/exports/results-window-excel'; import { makeSheet } from '../../src/importers/sheet-factory'; import { downloadBrowserBlob } from '../../src/platform/browser/browser-download-adapter'; vi.mock('../../src/platform/browser/browser-download-adapter', () => ({ downloadBrowserBlob: vi.fn() })); vi.mock('../../src/app/AppProvider', () => ({ useOptionalApp: () => undefined })); afterEach(() => { vi.clearAllMocks(); vi.unstubAllGlobals(); delete document.documentElement.dataset.theme; }); const description = 'Checks certificate templates allowing a subjectAltName in the CSR.'; const result = 'Found 17 published templates on which unprivileged users can write properties.'; const impact = 'Controlling templates allows issuance of an arbitrary certificate. References Original reference'; const finding = makeSheet('Synthetic indicator', [['Name','Description','Result Message','Number of Results','Likelihood of Compromise','Result'], ['Certificate templates',description,result,'17',impact,'Failed']], '', 'synthetic.csv').analysis.records[0]; const expected = [['Description','Description',description],['Problem','Result',result],['Problem','Affected','17'],['Problem','Impact',impact]]; async function workbook(blob: Blob) { const bytes = await new Promise((resolve,reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as ArrayBuffer); reader.onerror = reject; reader.readAsArrayBuffer(blob); }); return read(bytes,{type:'array'}); } it.each(['light','dark'])('includes the complete original finding context in actual all and selected downloads (%s)', async theme => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT',true);vi.stubGlobal('ResizeObserver',class { observe() {} disconnect() {} }); document.documentElement.dataset.theme=theme; const sheets = [makeSheet('Objects',[['Object'],['DUMMY-01'],['DUMMY-02']],'','synthetic.csv'),makeSheet('Second table',[['Other'],['DUMMY-03']],'','synthetic.csv')]; const before=JSON.stringify({finding,sheets});const host=document.createElement('div');document.body.append(host);const root=createRoot(host); try { await act(async () => root.render({}} onClose={()=>{}}/>)); await act(async () => host.querySelector('[aria-label="Export entire Results to Excel"]')!.click()); const all=await workbook(vi.mocked(downloadBrowserBlob).mock.calls.at(-1)![0]); expect(all.SheetNames.slice(0,2)).toEqual(['Results','Results 02']); expect(utils.sheet_to_json(all.Sheets['Finding context'],{header:1}).slice(2)).toEqual(expected); expect(all.Sheets['Finding context'].A1.v).toBe('Certificate templates'); await act(async () => host.querySelector('input[type="checkbox"]')!.click()); await act(async () => host.querySelector('[aria-label="Export selected tables to Excel"]')!.click()); const subset=await workbook(vi.mocked(downloadBrowserBlob).mock.calls.at(-1)![0]); expect(subset.SheetNames).toHaveLength(3);expect(subset.SheetNames[1]).toBe('Finding context'); expect(utils.sheet_to_json(subset.Sheets['Finding context'],{header:1}).slice(2)).toEqual(expected); expect(subset.SheetNames).not.toContain(all.SheetNames[1]); expect(JSON.stringify({finding,sheets})).toBe(before); } finally {await act(async()=>root.unmount());host.remove();} }); it('preserves long descriptions completely and handles missing context without inventing numbers',()=>{ const long='Long description 🧪 '.repeat(2500);const copy={...finding,fields:finding.fields.map(field=>field.label==='Description'?{...field,value:long}:field)}; const context=resultsFindingContext(copy,'purple-knight',[],'fail'); const wb=read(buildResultsWindowExcel({title:'Text only',text:'Result',tables:[],sourceSections:[],findingContext:context}),{type:'array'}); expect(wb.SheetNames[0]).toBe('Results'); const parts=utils.sheet_to_json(wb.Sheets['Long text'],{header:1}).slice(2); expect(parts.filter(row=>row[0]==='Finding context'&&row[1]==='3'&&row[2]==='3').map(row=>row[4]).join('')).toBe(long); expect(resultsFindingContext(undefined)).toEqual([]); const generic={...finding,fields:[{label:'Description',value:'Original',index:0},{label:'Result',value:'Failed',index:1}]}; expect(resultsFindingContext(generic,'unknown')).toEqual([{section:'Description',label:'Description',value:'Original'}]); }); it('retains the existing GPO problem comparison and backup scope without re-evaluation',()=>{ const policy={...finding,fields:[{label:'Description',value:'Original policy explanation',index:0},{label:'Status',value:'Fail',index:1}], policyBaselineComparisons:[{baseline:'Synthetic baseline',status:'Fail',summary:'Stored comparison',context:'Backup only',scopeNotice:'Not effective host state',explanation:'Source',remediation:'Original guidance', recommendations:[{template:'Synthetic',settingId:'s1',comparison:'different',expected:'Disabled',additional:[],missing:[]}]}]}; const before=JSON.stringify(policy);const context=resultsFindingContext(policy,'group-policy',[],'fail'); expect(context).toContainEqual({section:'Description',label:'Description',value:'Original policy explanation'}); expect(context).toContainEqual({section:'Problem',label:'Expected',value:'Disabled'}); expect(context.some(row=>row.label==='Affected')).toBe(false); expect(JSON.stringify(policy)).toBe(before); });