fix: restore console input and attachment workflows
Restore durable busy Submit queues and TUI Enter delivery, expose Web Queue beside Notify, and separate the Composer toolbar from its editor without persistent success notices. Support pasted image attachments and restore canonical Worker Session uploaded-file operations, including retention, binding, cleanup and authenticated upload context. Validated Worker, Protocol and Session Store suites, 278 TUI tests, 59 Web tests and Svelte check. Runtime suite could not complete due to external Workdir transport timeouts.
This commit is contained in:
@@ -3,11 +3,14 @@ import fixtureJson from "../../../tests/fixtures/composer-paste-policy.json" wit
|
||||
};
|
||||
|
||||
import {
|
||||
type ComposerPasteEvent,
|
||||
type ComposerPasteMeasurement,
|
||||
handleComposerImagePaste,
|
||||
handleComposerPaste,
|
||||
MAX_PLAIN_TEXT_PASTE_CHARS,
|
||||
MAX_PLAIN_TEXT_PASTE_LOGICAL_LINES,
|
||||
measureComposerPaste,
|
||||
namePastedImage,
|
||||
} from "../src/lib/workspace/console/composer-paste.ts";
|
||||
|
||||
declare const Deno: {
|
||||
@@ -30,6 +33,142 @@ function assertEquals(
|
||||
}
|
||||
}
|
||||
|
||||
function imagePasteEvent(clipboardData: ComposerPasteEvent["clipboardData"]) {
|
||||
let prevented = 0;
|
||||
return {
|
||||
event: {
|
||||
clipboardData,
|
||||
preventDefault: () => {
|
||||
prevented += 1;
|
||||
},
|
||||
},
|
||||
prevented: () => prevented,
|
||||
};
|
||||
}
|
||||
|
||||
Deno.test("image paste attaches original files once without reading alternate text", () => {
|
||||
const image = new File(["png"], "image.png", { type: "image/png" });
|
||||
const { event, prevented } = imagePasteEvent({
|
||||
getData: () => {
|
||||
throw new Error("image must take priority over text");
|
||||
},
|
||||
files: [image],
|
||||
items: [{ kind: "file", type: image.type, getAsFile: () => image }],
|
||||
});
|
||||
const batches: File[][] = [];
|
||||
assertEquals(
|
||||
handleComposerImagePaste(event, (files) => batches.push(files)),
|
||||
true,
|
||||
);
|
||||
assertEquals(prevented(), 1);
|
||||
assertEquals(batches.length, 1);
|
||||
assertEquals(batches[0].length, 1);
|
||||
assertEquals(batches[0][0] === image, true);
|
||||
});
|
||||
|
||||
Deno.test("image paste falls back to file items and ignores text, other files and null entries", () => {
|
||||
const image = new File(["jpeg"], "photo.jpg", { type: "image/jpeg" });
|
||||
const { event, prevented } = imagePasteEvent({
|
||||
getData: () => "alternate image title",
|
||||
files: [],
|
||||
items: [
|
||||
{ kind: "string", type: "text/plain", getAsFile: () => null },
|
||||
{
|
||||
kind: "file",
|
||||
type: "application/pdf",
|
||||
getAsFile: () => new File([], "a.pdf"),
|
||||
},
|
||||
{ kind: "file", type: "image/png", getAsFile: () => null },
|
||||
{ kind: "file", type: image.type, getAsFile: () => image },
|
||||
],
|
||||
});
|
||||
let files: File[] = [];
|
||||
assertEquals(
|
||||
handleComposerImagePaste(event, (value) => {
|
||||
files = value;
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assertEquals(files.length, 1);
|
||||
assertEquals(files[0] === image, true);
|
||||
assertEquals(prevented(), 1);
|
||||
});
|
||||
|
||||
Deno.test("multiple pasted images retain order and use attachment validation for unsupported types", () => {
|
||||
const images = ["image/png", "image/webp", "image/bmp"].map((type) =>
|
||||
new File([type], "image", { type })
|
||||
);
|
||||
const { event } = imagePasteEvent({
|
||||
getData: () => "",
|
||||
files: [
|
||||
images[0],
|
||||
new File([], "note.txt", { type: "text/plain" }),
|
||||
...images.slice(1),
|
||||
],
|
||||
});
|
||||
let attached: File[] = [];
|
||||
assertEquals(
|
||||
handleComposerImagePaste(event, (files) => {
|
||||
attached = files;
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assertEquals(attached.length, images.length);
|
||||
assertEquals(attached.every((file, index) => file === images[index]), true);
|
||||
});
|
||||
|
||||
Deno.test("non-image or absent clipboard leaves text paste and its exact content untouched", () => {
|
||||
for (
|
||||
const clipboardData of [null, { getData: () => " text\r\n" }, {
|
||||
getData: () => "file name",
|
||||
files: [new File([], "file.pdf", { type: "application/pdf" })],
|
||||
}]
|
||||
) {
|
||||
const { event, prevented } = imagePasteEvent(clipboardData);
|
||||
assertEquals(
|
||||
handleComposerImagePaste(event, () => {
|
||||
throw new Error("unexpected attachment");
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assertEquals(prevented(), 0);
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("image paste does not attach when disabled or no callback is installed", () => {
|
||||
const { event, prevented } = imagePasteEvent({
|
||||
getData: () => "",
|
||||
files: [new File(["png"], "image.png", { type: "image/png" })],
|
||||
});
|
||||
assertEquals(handleComposerImagePaste(event, undefined), false);
|
||||
assertEquals(
|
||||
handleComposerImagePaste(event, () => {
|
||||
throw new Error("disabled attachment");
|
||||
}, false),
|
||||
false,
|
||||
);
|
||||
assertEquals(prevented(), 0);
|
||||
});
|
||||
|
||||
Deno.test("pasted screenshot names are unique without changing bytes or media type", async () => {
|
||||
const file = new File([new Uint8Array([137, 80, 78, 71])], "image.png", {
|
||||
type: "image/png",
|
||||
lastModified: 1234,
|
||||
});
|
||||
const first = namePastedImage(file);
|
||||
const second = namePastedImage(file);
|
||||
assertEquals(first.name === second.name, false);
|
||||
assertEquals(first.name.endsWith("-image.png"), true);
|
||||
assertEquals(first.type, file.type);
|
||||
assertEquals(first.lastModified, file.lastModified);
|
||||
assertEquals(Array.from(new Uint8Array(await first.arrayBuffer())), [
|
||||
137,
|
||||
80,
|
||||
78,
|
||||
71,
|
||||
]);
|
||||
});
|
||||
|
||||
interface FixturePart {
|
||||
value: string;
|
||||
repeat: number;
|
||||
|
||||
Reference in New Issue
Block a user