60 lines
6.1 KiB
JavaScript
60 lines
6.1 KiB
JavaScript
// Actual browser downloads from synthetic Results; no datastore access or mutation.
|
||
import assert from 'node:assert/strict';
|
||
import { readFile } from 'node:fs/promises';
|
||
import { createServer } from 'node:http';
|
||
import { pathToFileURL } from 'node:url';
|
||
import { build } from 'esbuild';
|
||
import { read, utils } from 'xlsx';
|
||
const { chromium } = await import(pathToFileURL(process.env.PLAYWRIGHT_MODULE).href);
|
||
const main = await readFile('src/main.tsx', 'utf8');
|
||
const styles = [...main.matchAll(/^import '([^']+\.css)';/gm)].map(([, path]) => `import ${JSON.stringify(path.startsWith('.') ? './src/' + path.slice(2) : path)};`).join('\n');
|
||
const bundle = await build({ stdin: { resolveDir: process.cwd(), loader: 'tsx', contents: `${styles}
|
||
import React from 'react';import {createRoot} from 'react-dom/client';
|
||
import {FloatingResultsWindow} from './src/features/linked-results/FloatingResultsWindow';
|
||
import {makeSheet} from './src/importers/sheet-factory';
|
||
const rows=Array.from({length:1001},(_,i)=>['OBJECT-'+(i+1),'000'+i]);
|
||
const sheet=makeSheet('Synthetic linked',[['Object','Value'],...rows],'','synthetic.xlsx');
|
||
const finding=makeSheet('Synthetic indicator',[['Name','Description','Result Message','Number of Results','Likelihood of Compromise'],['Complete synthetic evidence','Original certificate description','Found 17 affected templates','17','Original impact explanation']],'','synthetic.csv').analysis.records[0];
|
||
const root=createRoot(document.getElementById('root'));
|
||
root.render(<FloatingResultsWindow id="synthetic" title="Results – Complete synthetic evidence" sheets={[sheet]} finding={finding} toolId="purple-knight" outcome="fail"
|
||
sections={[{title:'Source result',value:'Complete prose\\n\\n| Object | Status |\\n| --- | --- |\\n'+rows.map(([name])=>'| '+name+' | Failed |').join('\\n')},
|
||
{title:'Native HTML evidence',value:'<table><thead><tr><th>Object</th></tr></thead><tbody>'+rows.map(([name])=>'<tr><td>'+name+'</td></tr>').join('')+'</tbody></table>'}]}
|
||
color="" zIndex={1} offset={0} search="OBJECT-1001" onActivate={()=>{}} onClose={()=>{}} onToggleBookmark={()=>{}}/>);` }, jsx:'automatic',bundle:true,write:false,outdir:'synthetic',format:'iife',loader:{'.woff2':'dataurl','.woff':'dataurl','.png':'dataurl','.svg':'dataurl'} });
|
||
const server=createServer((req,res)=>{const file=bundle.outputFiles.find(file=>req.url==='/'+file.path.split(/[\\/]/).at(-1));res.setHeader('Content-Type',file?(req.url.endsWith('.css')?'text/css':'text/javascript'):'text/html');res.end(file?.contents??'<!doctype html><html><head><meta charset="utf-8"><link rel="stylesheet" href="/stdin.css"></head><body><div id="root"></div><script src="/stdin.js"></script></body></html>');});
|
||
await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));let browser;
|
||
async function workbook(download) {const stream=await download.createReadStream();const chunks=[];for await(const chunk of stream)chunks.push(chunk);return read(Buffer.concat(chunks),{type:'buffer'});}
|
||
try {
|
||
browser=await chromium.launch({headless:true,channel:'chrome'});
|
||
const page=await browser.newPage({viewport:{width:1280,height:900}});
|
||
const errors=[];page.on('pageerror',error=>errors.push(error.message));
|
||
await page.goto('http://127.0.0.1:'+server.address().port+'/');
|
||
await page.waitForSelector('table');
|
||
const all=page.getByRole('button',{name:'Export entire Results to Excel',exact:true});
|
||
const selected=page.getByRole('button',{name:'Export selected tables to Excel',exact:true});
|
||
for(const theme of ['dark','light']) {
|
||
await page.evaluate(theme=>document.documentElement.dataset.theme=theme,theme);
|
||
assert.deepEqual(await page.locator('table').evaluateAll(tables=>tables.map(table=>table.tBodies[0].rows.length)),[1001,1001,1001]);
|
||
assert.equal(await all.isEnabled(),true);assert.equal(await selected.isEnabled(),false);
|
||
const grey=await selected.evaluate(button=>getComputedStyle(button).color);
|
||
const entire=page.waitForEvent('download');await all.click();const full=await workbook(await entire);
|
||
const tableNames=full.SheetNames.filter(name=>/^Results(?: \d+)?$/.test(name));assert.equal(tableNames.length,3);
|
||
assert.deepEqual(full.SheetNames.slice(0,3),tableNames);
|
||
assert.match(full.Sheets[tableNames[0]].A1.v,/Complete synthetic evidence/);
|
||
for(const name of tableNames){const rows=utils.sheet_to_json(full.Sheets[name],{header:1});assert.equal(rows.length,name==='Results'?1006:1003);assert.equal(rows[1002][0],'OBJECT-1001');}
|
||
assert.match(full.Sheets.Results.A1006.v,/Complete prose/);assert.equal(full.SheetNames.includes('Results text'),false);
|
||
for(const name of full.SheetNames)assert.match(full.Sheets[name].A1.v,/Complete synthetic evidence/);
|
||
assert.equal(full.Sheets['Finding context'].C3.v,'Original certificate description');
|
||
assert.equal(full.Sheets['Finding context'].C5.v,'17');
|
||
await page.getByRole('checkbox',{name:'Select table for Excel export'}).nth(1).check();
|
||
assert.equal(await selected.isEnabled(),true);
|
||
const colors=await page.locator('.results-window-export').evaluateAll(buttons=>buttons.map(button=>getComputedStyle(button).color));
|
||
assert.notEqual(colors[0],colors[1]);assert.notEqual(colors[1],grey);
|
||
const subsetDownload=page.waitForEvent('download');await selected.click();const subset=await workbook(await subsetDownload);
|
||
assert.equal(subset.SheetNames.length,3);assert.equal(subset.SheetNames[0],'Results');assert.equal(subset.SheetNames[1],'Finding context');assert.equal(subset.SheetNames[2],'Export info');assert.equal(subset.Sheets[subset.SheetNames[0]].A1003.v,'OBJECT-1001');
|
||
assert.deepEqual(utils.sheet_to_json(subset.Sheets['Finding context'],{header:1}),utils.sheet_to_json(full.Sheets['Finding context'],{header:1}));
|
||
await page.getByRole('checkbox',{name:'Select table for Excel export'}).nth(1).uncheck();assert.equal(await selected.isEnabled(),false);
|
||
}
|
||
assert.deepEqual(errors,[]);
|
||
console.log('Both themes: all 1001 rows shown and downloaded, entire Results text included, selective export exact, distinct icon colors and disabled state verified.');
|
||
} finally {await browser?.close();await new Promise(resolve=>server.close(resolve));}
|