1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 nextPastedTextFileName,
5 PASTED_TEXT_ATTACHMENT_THRESHOLD_BYTES,
6 isPasteAsTextShortcut,
7 pastedTextDisposition,
8 replaceTextSelection,
9 wouldTextPasteExceedLimit,
10} from "./textPaste.ts";
11
12describe("pasted text disposition", () => {
13 it("keeps ordinary text inline and folds at the 32 KiB boundary", () => {
14 expect(
15 pastedTextDisposition({
16 text: "x".repeat(PASTED_TEXT_ATTACHMENT_THRESHOLD_BYTES - 1),
17 canAttach: true,
18 }),
19 ).toBe("inline");
20 expect(
21 pastedTextDisposition({
22 text: "x".repeat(PASTED_TEXT_ATTACHMENT_THRESHOLD_BYTES),
23 canAttach: true,
24 }),
25 ).toBe("attachment");
26 });
27
28 it("measures UTF-8 bytes instead of UTF-16 characters", () => {
29 const text = "🙂".repeat(PASTED_TEXT_ATTACHMENT_THRESHOLD_BYTES / 4);
30 expect(text.length).toBeLessThan(PASTED_TEXT_ATTACHMENT_THRESHOLD_BYTES);
31 expect(pastedTextDisposition({ text, canAttach: true })).toBe("attachment");
32 });
33
34 it("keeps text inline for the explicit bypass or without attachment support", () => {
35 const text = "x".repeat(PASTED_TEXT_ATTACHMENT_THRESHOLD_BYTES);
36 expect(pastedTextDisposition({ text, canAttach: true, bypassAutoAttachment: true })).toBe(
37 "inline",
38 );
39 expect(pastedTextDisposition({ text, canAttach: false })).toBe("inline");
40 });
41
42 it("folds a smaller paste when the resulting prompt would exceed the input limit", () => {
43 expect(
44 pastedTextDisposition({
45 text: "small paste",
46 canAttach: true,
47 wouldExceedInputLimit: true,
48 }),
49 ).toBe("attachment");
50 });
51});
52
53describe("pasted text attachment names", () => {
54 it("uses the first available readable sequence", () => {
55 expect(nextPastedTextFileName([])).toBe("pasted-text.txt");
56 expect(nextPastedTextFileName(["PASTED-TEXT.TXT", "pasted-text-2.txt"])).toBe(
57 "pasted-text-3.txt",
58 );
59 });
60});
61
62describe("paste-as-text shortcut", () => {
63 it.each([
64 { metaKey: true, ctrlKey: false, macPlatform: true },
65 { metaKey: false, ctrlKey: true, macPlatform: false },
66 ])("accepts the platform modifier with Shift", ({ metaKey, ctrlKey, macPlatform }) => {
67 expect(
68 isPasteAsTextShortcut(
69 {
70 key: "V",
71 metaKey,
72 ctrlKey,
73 shiftKey: true,
74 altKey: false,
75 },
76 macPlatform,
77 ),
78 ).toBe(true);
79 });
80
81 it("does not claim ordinary or alternate paste chords", () => {
82 expect(
83 isPasteAsTextShortcut(
84 {
85 key: "v",
86 metaKey: true,
87 ctrlKey: false,
88 shiftKey: false,
89 altKey: false,
90 },
91 true,
92 ),
93 ).toBe(false);
94 expect(
95 isPasteAsTextShortcut(
96 {
97 key: "v",
98 metaKey: true,
99 ctrlKey: false,
100 shiftKey: true,
101 altKey: true,
102 },
103 true,
104 ),
105 ).toBe(false);
106 });
107
108 it("rejects the other platform's modifier", () => {
109 const event = {
110 key: "v",
111 metaKey: false,
112 ctrlKey: true,
113 shiftKey: true,
114 altKey: false,
115 };
116 expect(isPasteAsTextShortcut(event, true)).toBe(false);
117 expect(isPasteAsTextShortcut({ ...event, metaKey: true, ctrlKey: false }, false)).toBe(false);
118 });
119});
120
121describe("text paste insertion", () => {
122 it("replaces the selected range and returns the collapsed cursor", () => {
123 expect(
124 replaceTextSelection({
125 value: "before old after",
126 selection: { start: 7, end: 10 },
127 text: "new",
128 }),
129 ).toEqual({ value: "before new after", cursor: 10 });
130 });
131
132 it("clamps stale native selections", () => {
133 expect(
134 replaceTextSelection({ value: "abc", selection: { start: 20, end: 30 }, text: "!" }),
135 ).toEqual({ value: "abc!", cursor: 4 });
136 });
137
138 it("measures the replacement value after removing the selected range", () => {
139 expect(
140 wouldTextPasteExceedLimit({
141 valueLength: 100,
142 selection: { start: 40, end: 80 },
143 textLength: 30,
144 maxLength: 100,
145 }),
146 ).toBe(false);
147 expect(
148 wouldTextPasteExceedLimit({
149 valueLength: 100,
150 selection: { start: 40, end: 80 },
151 textLength: 41,
152 maxLength: 100,
153 }),
154 ).toBe(true);
155 });
156});