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, /