Deploy contracts-flask / validate (push) Canceled after 0s
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
111 lines
4.2 KiB
JavaScript
111 lines
4.2 KiB
JavaScript
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(`<script title="x">&'`), '<script title="x">&'');
|
|
}
|
|
|
|
function testStatusAndDocumentActionsRenderSafely() {
|
|
const node = {
|
|
id: 'file-1',
|
|
kind: 'file',
|
|
name: '<img src=x>',
|
|
path: '<img src=x>',
|
|
file: new File(['x'], '<img src=x>'),
|
|
children: [],
|
|
expanded: true,
|
|
};
|
|
const state = {
|
|
nodes: [node],
|
|
statuses: new Map([['file-1', { kind: 'error', text: '<script>bad</script>' }]]),
|
|
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, /<script>bad/);
|
|
assert.match(elements.tableBodyEl.innerHTML, /<img src=x>/);
|
|
}
|
|
|
|
const tests = [
|
|
['recursive ZIP tree and unsafe paths', testRecursiveZipTree],
|
|
['deduplication by path and size', testPathAndSizeDeduplication],
|
|
['obscure.lst selection filter', testObscureManifest],
|
|
['HTML escaping', testEscaping],
|
|
['status and document action rendering', testStatusAndDocumentActionsRenderSafely],
|
|
];
|
|
|
|
for (const [name, test] of tests) {
|
|
await test();
|
|
console.log(`PASS ${name}`);
|
|
}
|