import test from "node:test"; import assert from "node:assert/strict"; import {appendAttachments, createAttachmentComposer} from "../public/app/attachments.js"; class Element { constructor() { this.children = []; this.listeners = {}; this.classList = {add() {}, remove() {}}; } append(...children) { this.children.push(...children); } replaceChildren(...children) { this.children = children; } setAttribute() {} addEventListener(name, listener) { this.listeners[name] = listener; } click() { this.clicked = true; } } test("composer preview, pending state, upload failure/retry, removal, paste, and history rendering", async t => { const originalDocument = globalThis.document, originalFetch = globalThis.fetch; t.after(() => { globalThis.document = originalDocument; globalThis.fetch = originalFetch; }); const elements = Object.fromEntries(["attachImage", "imagePicker", "uploadPreviews", "composer"].map(id => [id, new Element()])); globalThis.document = {getElementById: id => elements[id], querySelector: () => elements.composer, createElement: () => new Element()}; let completeUpload; let changes = 0; globalThis.fetch = async (url) => { if (url === "/uploads/config") return Response.json({maxFiles: 5, maxSizeMb: 10}); if (url === "/uploads") return new Promise(resolve => { completeUpload = resolve; }); return Response.json({success: true}); }; const composer = createAttachmentComposer({onChange: () => changes++, onError: message => assert.fail(message)}); elements.attachImage.onclick(); assert.equal(elements.imagePicker.clicked, true); elements.imagePicker.files = [new File(["image fixture"], "screenshot.png", {type: "image/png"})]; elements.imagePicker.onchange(); assert.equal(composer.pending, true); assert.equal(composer.attachments.length, 0); assert.equal(elements.uploadPreviews.children.length, 1); const tick = () => new Promise(resolve => setTimeout(resolve, 0)); completeUpload(Response.json({error: "Malformed image"}, {status: 400})); await tick(); assert.match(elements.uploadPreviews.children[0].children[1].textContent, /Malformed image/); elements.uploadPreviews.children[0].children[2].onclick(); const metadata = {id: "12345678-1234-1234-1234-123456789abc", originalName: "screenshot.png"}; completeUpload(Response.json(metadata, {status: 201})); await tick(); assert.equal(composer.pending, false); assert.deepEqual(composer.attachments, [metadata]); composer.setBusy(true); assert.equal(elements.attachImage.disabled, true); composer.setBusy(false); elements.uploadPreviews.children[0].children.at(-1).onclick(); assert.equal(composer.attachments.length, 0); assert.equal(elements.uploadPreviews.hidden, true); let prevented = false; elements.composer.listeners.paste({clipboardData: {files: [], getData: () => "text"}, preventDefault: () => { prevented = true; }}); assert.equal(prevented, false); const history = new Element(); appendAttachments(history); assert.equal(history.children.length, 0); appendAttachments(history, [metadata]); assert.equal(history.children[0].children[0].href, `/attachments/${metadata.id}`); assert.ok(changes > 4); });