import assert from 'node:assert/strict';
import { zipSync } from 'fflate';
import { listZipFiles } from './zip/list_zip_files.js';
import { addFileWithDedup } from './table/add_file_with_dedup.js';
import { flattenFiles, render } from './table/render.js';
import { lstFilter } from './table/lst_filter.js';
import { esc } from './table/esc.js';
if (typeof globalThis.File === 'undefined') {
globalThis.File = class File extends Blob {
constructor(parts, name, options = {}) {
super(parts, options);
this.name = name;
this.lastModified = options.lastModified || Date.now();
}
};
}
globalThis.crypto = globalThis.crypto || { randomUUID: () => 'test-picker-id' };
function bytes(text) {
return new TextEncoder().encode(text);
}
function withRelativePath(file, path) {
Object.defineProperty(file, 'webkitRelativePath', { value: path });
return file;
}
async function testRecursiveZipTree() {
const nested = zipSync({ 'inside.pdf': bytes('nested') });
const archive = zipSync({
'contracts/one.pdf': bytes('one'),
'contracts/ignore.txt': bytes('text'),
'nested.zip': nested,
'../escape.pdf': bytes('unsafe'),
});
const tree = await listZipFiles(new File([archive], 'outer.zip'), ['.pdf', '.doc', '.docx']);
const files = flattenFiles([tree]);
assert.deepEqual(files.map((file) => file.path).sort(), [
'outer.zip/contracts/one.pdf',
'outer.zip/nested.zip/inside.pdf',
]);
assert.equal(files.find((file) => file.name === 'inside.pdf').file.name, 'inside.pdf');
}
function testPathAndSizeDeduplication() {
const state = { nodes: [], fileKeys: new Set() };
const file = new File(['same'], 'a.pdf');
const node = { id: 'one', kind: 'file', name: 'a.pdf', path: 'a.pdf', file, children: [] };
assert.equal(addFileWithDedup(state, node), true);
assert.equal(addFileWithDedup(state, {
...node, id: 'two', file: new File(['same'], 'a.pdf'),
}), false);
assert.equal(addFileWithDedup(state, {
...node, id: 'three', path: 'folder/a.pdf',
}), true);
assert.deepEqual(flattenFiles(state.nodes).map((entry) => entry.path), ['a.pdf', 'folder/a.pdf']);
}
async function testObscureManifest() {
const manifest = withRelativePath(new File(['contracts/**\n!**/skip.docx\n'], 'obscure.lst'), 'root/obscure.lst');
const included = withRelativePath(new File(['a'], 'a.pdf'), 'root/contracts/a.pdf');
const excluded = withRelativePath(new File(['b'], 'skip.docx'), 'root/contracts/skip.docx');
const outside = withRelativePath(new File(['c'], 'outside.pdf'), 'root/other/outside.pdf');
const result = await lstFilter([manifest, included, excluded, outside], ['.pdf', '.doc', '.docx', '.zip']);
assert.equal(result.manifest, manifest);
assert.deepEqual(result.files, [included]);
assert.deepEqual(result.diagnostics, []);
}
function testEscaping() {
assert.equal(esc(`' }]]),
documents: new Map([['file-1', 'doc-1']]),
};
const elements = { tableBodyEl: { innerHTML: '' }, countEl: { textContent: '' } };
render(state, elements);
assert.match(elements.tableBodyEl.innerHTML, /data-details="file-1"/);
assert.match(elements.tableBodyEl.innerHTML, /data-text="file-1"/);
assert.match(elements.tableBodyEl.innerHTML, /<script>bad<\/script>/);
assert.doesNotMatch(elements.tableBodyEl.innerHTML, /