@uploadista/vue 0.0.20 → 0.1.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.d.mts +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/{components-CskPs6sR.css → components-B_L33hsM.css} +33 -33
- package/dist/{components-CskPs6sR.css.map → components-B_L33hsM.css.map} +1 -1
- package/dist/components-MZ9ETx9c.mjs +2 -0
- package/dist/components-MZ9ETx9c.mjs.map +1 -0
- package/dist/composables/index.d.mts +1 -1
- package/dist/composables/index.mjs +1 -1
- package/dist/composables-Dny_9Zrg.mjs +2 -0
- package/dist/composables-Dny_9Zrg.mjs.map +1 -0
- package/dist/{index-CLOy812P.d.mts → index-6Scxoy1b.d.mts} +412 -408
- package/dist/index-6Scxoy1b.d.mts.map +1 -0
- package/dist/{index-CDJUpsAf.d.mts → index-BpCRFLJ5.d.mts} +8 -8
- package/dist/index-BpCRFLJ5.d.mts.map +1 -0
- package/dist/{index-BSlqFF1H.d.mts → index-RY4FPqAk.d.mts} +431 -431
- package/dist/index-RY4FPqAk.d.mts.map +1 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/providers/index.d.mts +1 -1
- package/dist/providers/index.mjs +1 -1
- package/dist/{providers-fqmOwF71.mjs → providers-CjhEBaQV.mjs} +2 -2
- package/dist/providers-CjhEBaQV.mjs.map +1 -0
- package/dist/useUploadistaClient-WVuo8jYH.mjs.map +1 -1
- package/dist/utils/index.d.mts +62 -2
- package/dist/utils/index.d.mts.map +1 -0
- package/package.json +11 -9
- package/src/__tests__/setup.ts +154 -0
- package/src/components/FlowUploadList.vue +25 -24
- package/src/components/UploadList.vue +3 -6
- package/src/components/UploadZone.vue +2 -5
- package/src/components/flow/Flow.vue +16 -4
- package/src/components/flow/FlowDropZone.vue +4 -2
- package/src/components/flow/FlowInput.vue +14 -8
- package/src/components/flow/FlowInputDropZone.vue +4 -2
- package/src/components/flow/FlowInputPreview.vue +3 -1
- package/src/components/flow/FlowProgress.vue +1 -1
- package/src/components/flow/FlowStatus.vue +1 -1
- package/src/components/flow/useFlowContext.ts +7 -5
- package/src/components/index.ts +2 -3
- package/src/components/upload/Upload.vue +16 -5
- package/src/components/upload/UploadCancel.vue +2 -4
- package/src/components/upload/UploadClearCompleted.vue +1 -1
- package/src/components/upload/UploadDropZone.vue +7 -7
- package/src/components/upload/UploadError.vue +2 -4
- package/src/components/upload/UploadItem.vue +8 -6
- package/src/components/upload/UploadItems.vue +2 -4
- package/src/components/upload/UploadProgress.vue +2 -4
- package/src/components/upload/UploadReset.vue +2 -4
- package/src/components/upload/UploadRetry.vue +2 -4
- package/src/components/upload/UploadStartAll.vue +6 -6
- package/src/components/upload/UploadStatus.vue +2 -4
- package/src/components/upload/index.ts +21 -25
- package/src/composables/eventUtils.test.ts +267 -0
- package/src/composables/eventUtils.ts +5 -4
- package/src/composables/index.ts +1 -1
- package/src/composables/useDragDrop.test.ts +304 -0
- package/src/composables/useFlow.ts +6 -2
- package/src/composables/useFlowManagerContext.ts +5 -1
- package/src/composables/useUploadEvents.ts +1 -4
- package/src/composables/useUploadistaClient.test.ts +152 -0
- package/src/index.ts +43 -45
- package/src/providers/FlowManagerProvider.vue +5 -2
- package/src/utils/index.test.ts +396 -0
- package/src/utils/is-browser-file.test.ts +45 -0
- package/vitest.config.ts +25 -0
- package/dist/components-DoBB6sqm.mjs +0 -2
- package/dist/components-DoBB6sqm.mjs.map +0 -1
- package/dist/composables-BZ2c_WgI.mjs +0 -2
- package/dist/composables-BZ2c_WgI.mjs.map +0 -1
- package/dist/index-BLNNvTVx.d.mts +0 -62
- package/dist/index-BLNNvTVx.d.mts.map +0 -1
- package/dist/index-BSlqFF1H.d.mts.map +0 -1
- package/dist/index-CDJUpsAf.d.mts.map +0 -1
- package/dist/index-CLOy812P.d.mts.map +0 -1
- package/dist/providers-fqmOwF71.mjs.map +0 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { isFlowEvent, isUploadEvent } from "./eventUtils";
|
|
3
|
+
|
|
4
|
+
describe("eventUtils", () => {
|
|
5
|
+
describe("isFlowEvent", () => {
|
|
6
|
+
it("should return true for JobStart event", () => {
|
|
7
|
+
const event = { eventType: "job-start", jobId: "job-1" };
|
|
8
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should return true for JobEnd event", () => {
|
|
12
|
+
const event = { eventType: "job-end", jobId: "job-1" };
|
|
13
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should return true for FlowStart event", () => {
|
|
17
|
+
const event = {
|
|
18
|
+
eventType: "flow-start",
|
|
19
|
+
jobId: "job-1",
|
|
20
|
+
flowId: "flow-1",
|
|
21
|
+
};
|
|
22
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should return true for FlowEnd event", () => {
|
|
26
|
+
const event = {
|
|
27
|
+
eventType: "flow-end",
|
|
28
|
+
jobId: "job-1",
|
|
29
|
+
flowId: "flow-1",
|
|
30
|
+
};
|
|
31
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should return true for FlowError event", () => {
|
|
35
|
+
const event = {
|
|
36
|
+
eventType: "flow-error",
|
|
37
|
+
jobId: "job-1",
|
|
38
|
+
flowId: "flow-1",
|
|
39
|
+
error: "Something went wrong",
|
|
40
|
+
};
|
|
41
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should return true for FlowPause event", () => {
|
|
45
|
+
const event = {
|
|
46
|
+
eventType: "flow-pause",
|
|
47
|
+
jobId: "job-1",
|
|
48
|
+
flowId: "flow-1",
|
|
49
|
+
};
|
|
50
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should return true for FlowCancel event", () => {
|
|
54
|
+
const event = {
|
|
55
|
+
eventType: "flow-cancel",
|
|
56
|
+
jobId: "job-1",
|
|
57
|
+
flowId: "flow-1",
|
|
58
|
+
};
|
|
59
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should return true for NodeStart event", () => {
|
|
63
|
+
const event = {
|
|
64
|
+
eventType: "node-start",
|
|
65
|
+
jobId: "job-1",
|
|
66
|
+
flowId: "flow-1",
|
|
67
|
+
nodeId: "node-1",
|
|
68
|
+
nodeName: "input-node",
|
|
69
|
+
nodeType: "input",
|
|
70
|
+
};
|
|
71
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should return true for NodeEnd event", () => {
|
|
75
|
+
const event = {
|
|
76
|
+
eventType: "node-end",
|
|
77
|
+
jobId: "job-1",
|
|
78
|
+
flowId: "flow-1",
|
|
79
|
+
nodeId: "node-1",
|
|
80
|
+
nodeName: "input-node",
|
|
81
|
+
};
|
|
82
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should return true for NodePause event", () => {
|
|
86
|
+
const event = {
|
|
87
|
+
eventType: "node-pause",
|
|
88
|
+
jobId: "job-1",
|
|
89
|
+
flowId: "flow-1",
|
|
90
|
+
nodeId: "node-1",
|
|
91
|
+
nodeName: "input-node",
|
|
92
|
+
};
|
|
93
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should return true for NodeResume event", () => {
|
|
97
|
+
const event = {
|
|
98
|
+
eventType: "node-resume",
|
|
99
|
+
jobId: "job-1",
|
|
100
|
+
flowId: "flow-1",
|
|
101
|
+
nodeId: "node-1",
|
|
102
|
+
nodeName: "input-node",
|
|
103
|
+
nodeType: "input",
|
|
104
|
+
};
|
|
105
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should return true for NodeError event", () => {
|
|
109
|
+
const event = {
|
|
110
|
+
eventType: "node-error",
|
|
111
|
+
jobId: "job-1",
|
|
112
|
+
flowId: "flow-1",
|
|
113
|
+
nodeId: "node-1",
|
|
114
|
+
nodeName: "input-node",
|
|
115
|
+
error: "Node failed",
|
|
116
|
+
};
|
|
117
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should return true for NodeStream event", () => {
|
|
121
|
+
const event = {
|
|
122
|
+
eventType: "node-stream",
|
|
123
|
+
jobId: "job-1",
|
|
124
|
+
flowId: "flow-1",
|
|
125
|
+
nodeId: "node-1",
|
|
126
|
+
};
|
|
127
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should return true for NodeResponse event", () => {
|
|
131
|
+
const event = {
|
|
132
|
+
eventType: "node-response",
|
|
133
|
+
jobId: "job-1",
|
|
134
|
+
flowId: "flow-1",
|
|
135
|
+
nodeId: "node-1",
|
|
136
|
+
nodeName: "input-node",
|
|
137
|
+
data: {},
|
|
138
|
+
};
|
|
139
|
+
expect(isFlowEvent(event)).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should return false for upload events", () => {
|
|
143
|
+
const event = {
|
|
144
|
+
type: "upload-started",
|
|
145
|
+
data: { id: "upload-1" },
|
|
146
|
+
};
|
|
147
|
+
expect(isFlowEvent(event)).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should return false for events without eventType", () => {
|
|
151
|
+
const event = { type: "unknown" };
|
|
152
|
+
expect(isFlowEvent(event)).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should return false for unknown eventType values", () => {
|
|
156
|
+
const event = { eventType: "unknown-event" };
|
|
157
|
+
expect(isFlowEvent(event)).toBe(false);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("isUploadEvent", () => {
|
|
162
|
+
it("should return true for UPLOAD_STARTED event", () => {
|
|
163
|
+
const event = {
|
|
164
|
+
type: "upload-started",
|
|
165
|
+
data: { id: "upload-1", name: "test.txt", size: 100 },
|
|
166
|
+
};
|
|
167
|
+
expect(isUploadEvent(event)).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should return true for UPLOAD_PROGRESS event", () => {
|
|
171
|
+
const event = {
|
|
172
|
+
type: "upload-progress",
|
|
173
|
+
data: { id: "upload-1", progress: 50, total: 100 },
|
|
174
|
+
};
|
|
175
|
+
expect(isUploadEvent(event)).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should return true for UPLOAD_COMPLETE event", () => {
|
|
179
|
+
const event = {
|
|
180
|
+
type: "upload-complete",
|
|
181
|
+
data: { id: "upload-1", name: "test.txt", size: 100 },
|
|
182
|
+
};
|
|
183
|
+
expect(isUploadEvent(event)).toBe(true);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should return true for UPLOAD_FAILED event", () => {
|
|
187
|
+
const event = {
|
|
188
|
+
type: "upload-failed",
|
|
189
|
+
data: { id: "upload-1", error: "Upload failed" },
|
|
190
|
+
};
|
|
191
|
+
expect(isUploadEvent(event)).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("should return true for UPLOAD_VALIDATION_SUCCESS event", () => {
|
|
195
|
+
const event = {
|
|
196
|
+
type: "upload-validation-success",
|
|
197
|
+
data: { id: "upload-1", validationType: "checksum" },
|
|
198
|
+
};
|
|
199
|
+
expect(isUploadEvent(event)).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("should return true for UPLOAD_VALIDATION_FAILED event", () => {
|
|
203
|
+
const event = {
|
|
204
|
+
type: "upload-validation-failed",
|
|
205
|
+
data: {
|
|
206
|
+
id: "upload-1",
|
|
207
|
+
reason: "Checksum mismatch",
|
|
208
|
+
expected: "abc",
|
|
209
|
+
actual: "def",
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
expect(isUploadEvent(event)).toBe(true);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should return true for UPLOAD_VALIDATION_WARNING event", () => {
|
|
216
|
+
const event = {
|
|
217
|
+
type: "upload-validation-warning",
|
|
218
|
+
data: { id: "upload-1", message: "Warning message" },
|
|
219
|
+
};
|
|
220
|
+
expect(isUploadEvent(event)).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should return false for flow events", () => {
|
|
224
|
+
const event = {
|
|
225
|
+
eventType: "flow-start",
|
|
226
|
+
jobId: "job-1",
|
|
227
|
+
flowId: "flow-1",
|
|
228
|
+
};
|
|
229
|
+
expect(isUploadEvent(event)).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should return false for events without type", () => {
|
|
233
|
+
const event = { eventType: "flow-start" };
|
|
234
|
+
expect(isUploadEvent(event)).toBe(false);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("should return false for unknown type values", () => {
|
|
238
|
+
const event = { type: "unknown-type" };
|
|
239
|
+
expect(isUploadEvent(event)).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("combined behavior", () => {
|
|
244
|
+
it("should not classify the same event as both flow and upload", () => {
|
|
245
|
+
const flowEvent = {
|
|
246
|
+
eventType: "flow-start",
|
|
247
|
+
jobId: "job-1",
|
|
248
|
+
flowId: "flow-1",
|
|
249
|
+
};
|
|
250
|
+
const uploadEvent = {
|
|
251
|
+
type: "upload-started",
|
|
252
|
+
data: { id: "upload-1" },
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
expect(isFlowEvent(flowEvent)).toBe(true);
|
|
256
|
+
expect(isUploadEvent(flowEvent)).toBe(false);
|
|
257
|
+
|
|
258
|
+
expect(isUploadEvent(uploadEvent)).toBe(true);
|
|
259
|
+
expect(isFlowEvent(uploadEvent)).toBe(false);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should return false for empty objects", () => {
|
|
263
|
+
expect(isFlowEvent({})).toBe(false);
|
|
264
|
+
expect(isUploadEvent({})).toBe(false);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { UploadistaEvent } from "@uploadista/client-browser";
|
|
2
1
|
import { EventType, type FlowEvent } from "@uploadista/core/flow";
|
|
3
|
-
import {
|
|
2
|
+
import { type UploadEvent, UploadEventType } from "@uploadista/core/types";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Type guard to check if an event is a flow event
|
|
7
6
|
*/
|
|
8
|
-
export function isFlowEvent(event:
|
|
7
|
+
export function isFlowEvent(event: unknown): event is FlowEvent {
|
|
8
|
+
if (typeof event !== "object" || event === null) return false;
|
|
9
9
|
if (!("eventType" in event)) return false;
|
|
10
10
|
const e = event as { eventType: unknown };
|
|
11
11
|
return (
|
|
@@ -29,7 +29,8 @@ export function isFlowEvent(event: UploadistaEvent): event is FlowEvent {
|
|
|
29
29
|
/**
|
|
30
30
|
* Type guard to check if an event is an upload event
|
|
31
31
|
*/
|
|
32
|
-
export function isUploadEvent(event:
|
|
32
|
+
export function isUploadEvent(event: unknown): event is UploadEvent {
|
|
33
|
+
if (typeof event !== "object" || event === null) return false;
|
|
33
34
|
if (!("type" in event)) return false;
|
|
34
35
|
const e = event as { type: unknown };
|
|
35
36
|
return (
|
package/src/composables/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Event composables
|
|
2
2
|
export { isFlowEvent, isUploadEvent } from "./eventUtils";
|
|
3
|
-
export { useUploadistaEvents } from "./useUploadistaEvents";
|
|
4
3
|
export type { UseFlowEventsOptions } from "./useFlowEvents";
|
|
5
4
|
export { useFlowEvents } from "./useFlowEvents";
|
|
6
5
|
export type {
|
|
@@ -13,6 +12,7 @@ export type {
|
|
|
13
12
|
UseUploadEventsOptions,
|
|
14
13
|
} from "./useUploadEvents";
|
|
15
14
|
export { useUploadEvents } from "./useUploadEvents";
|
|
15
|
+
export { useUploadistaEvents } from "./useUploadistaEvents";
|
|
16
16
|
|
|
17
17
|
// Plugin
|
|
18
18
|
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
createMockDragEvent,
|
|
4
|
+
createMockFile,
|
|
5
|
+
createMockInputChangeEvent,
|
|
6
|
+
} from "../__tests__/setup";
|
|
7
|
+
import { useDragDrop } from "./useDragDrop";
|
|
8
|
+
|
|
9
|
+
describe("useDragDrop", () => {
|
|
10
|
+
describe("initial state", () => {
|
|
11
|
+
it("should have correct initial state", () => {
|
|
12
|
+
const { state } = useDragDrop();
|
|
13
|
+
|
|
14
|
+
expect(state.value.isDragging).toBe(false);
|
|
15
|
+
expect(state.value.isOver).toBe(false);
|
|
16
|
+
expect(state.value.isValid).toBe(true);
|
|
17
|
+
expect(state.value.errors).toEqual([]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("drag events", () => {
|
|
22
|
+
it("should set isDragging and isOver on dragenter", () => {
|
|
23
|
+
const onDragStateChange = vi.fn();
|
|
24
|
+
const { state, onDragEnter } = useDragDrop({ onDragStateChange });
|
|
25
|
+
|
|
26
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
27
|
+
|
|
28
|
+
expect(state.value.isDragging).toBe(true);
|
|
29
|
+
expect(state.value.isOver).toBe(true);
|
|
30
|
+
expect(onDragStateChange).toHaveBeenCalledWith(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should not trigger multiple times for nested elements", () => {
|
|
34
|
+
const onDragStateChange = vi.fn();
|
|
35
|
+
const { state, onDragEnter } = useDragDrop({ onDragStateChange });
|
|
36
|
+
|
|
37
|
+
// Multiple dragenter events (simulating nested elements)
|
|
38
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
39
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
40
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
41
|
+
|
|
42
|
+
expect(state.value.isDragging).toBe(true);
|
|
43
|
+
// onDragStateChange should only be called once
|
|
44
|
+
expect(onDragStateChange).toHaveBeenCalledTimes(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should reset state on dragleave when counter reaches 0", () => {
|
|
48
|
+
const onDragStateChange = vi.fn();
|
|
49
|
+
const { state, onDragEnter, onDragLeave } = useDragDrop({
|
|
50
|
+
onDragStateChange,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Enter twice (nested elements)
|
|
54
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
55
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
56
|
+
|
|
57
|
+
// Leave once - should still be dragging
|
|
58
|
+
onDragLeave(createMockDragEvent("dragleave"));
|
|
59
|
+
expect(state.value.isDragging).toBe(true);
|
|
60
|
+
|
|
61
|
+
// Leave again - now should be reset
|
|
62
|
+
onDragLeave(createMockDragEvent("dragleave"));
|
|
63
|
+
expect(state.value.isDragging).toBe(false);
|
|
64
|
+
expect(state.value.isOver).toBe(false);
|
|
65
|
+
expect(onDragStateChange).toHaveBeenLastCalledWith(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should set dropEffect on dragover", () => {
|
|
69
|
+
const { onDragOver } = useDragDrop();
|
|
70
|
+
const event = createMockDragEvent("dragover");
|
|
71
|
+
|
|
72
|
+
onDragOver(event);
|
|
73
|
+
|
|
74
|
+
expect(event.dataTransfer?.dropEffect).toBe("copy");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("drop handling", () => {
|
|
79
|
+
it("should process files on drop", () => {
|
|
80
|
+
const onFilesReceived = vi.fn();
|
|
81
|
+
const { state, onDragEnter, onDrop } = useDragDrop({ onFilesReceived });
|
|
82
|
+
|
|
83
|
+
const file = createMockFile("test.txt", 100, "text/plain");
|
|
84
|
+
|
|
85
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
86
|
+
onDrop(createMockDragEvent("drop", [file]));
|
|
87
|
+
|
|
88
|
+
expect(state.value.isDragging).toBe(false);
|
|
89
|
+
expect(state.value.isOver).toBe(false);
|
|
90
|
+
expect(onFilesReceived).toHaveBeenCalledWith([file]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should not call onFilesReceived when no files dropped", () => {
|
|
94
|
+
const onFilesReceived = vi.fn();
|
|
95
|
+
const { onDrop } = useDragDrop({ onFilesReceived });
|
|
96
|
+
|
|
97
|
+
onDrop(createMockDragEvent("drop", []));
|
|
98
|
+
|
|
99
|
+
expect(onFilesReceived).not.toHaveBeenCalled();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("file validation", () => {
|
|
104
|
+
it("should validate file type with extension", () => {
|
|
105
|
+
const onFilesReceived = vi.fn();
|
|
106
|
+
const onValidationError = vi.fn();
|
|
107
|
+
const { state, onDrop } = useDragDrop({
|
|
108
|
+
accept: [".txt"],
|
|
109
|
+
onFilesReceived,
|
|
110
|
+
onValidationError,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const validFile = createMockFile("test.txt", 100, "text/plain");
|
|
114
|
+
onDrop(createMockDragEvent("drop", [validFile]));
|
|
115
|
+
|
|
116
|
+
expect(onFilesReceived).toHaveBeenCalledWith([validFile]);
|
|
117
|
+
expect(onValidationError).not.toHaveBeenCalled();
|
|
118
|
+
expect(state.value.isValid).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should reject invalid file types", () => {
|
|
122
|
+
const onFilesReceived = vi.fn();
|
|
123
|
+
const onValidationError = vi.fn();
|
|
124
|
+
const { state, onDrop } = useDragDrop({
|
|
125
|
+
accept: [".txt"],
|
|
126
|
+
onFilesReceived,
|
|
127
|
+
onValidationError,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const invalidFile = createMockFile("test.pdf", 100, "application/pdf");
|
|
131
|
+
onDrop(createMockDragEvent("drop", [invalidFile]));
|
|
132
|
+
|
|
133
|
+
expect(onFilesReceived).not.toHaveBeenCalled();
|
|
134
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
135
|
+
expect(state.value.isValid).toBe(false);
|
|
136
|
+
expect(state.value.errors.length).toBeGreaterThan(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should validate file type with MIME wildcard", () => {
|
|
140
|
+
const onFilesReceived = vi.fn();
|
|
141
|
+
const { onDrop } = useDragDrop({
|
|
142
|
+
accept: ["image/*"],
|
|
143
|
+
onFilesReceived,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const jpgFile = createMockFile("photo.jpg", 100, "image/jpeg");
|
|
147
|
+
const pngFile = createMockFile("photo.png", 100, "image/png");
|
|
148
|
+
|
|
149
|
+
onDrop(createMockDragEvent("drop", [jpgFile]));
|
|
150
|
+
expect(onFilesReceived).toHaveBeenCalledWith([jpgFile]);
|
|
151
|
+
|
|
152
|
+
onDrop(createMockDragEvent("drop", [pngFile]));
|
|
153
|
+
expect(onFilesReceived).toHaveBeenCalledWith([pngFile]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should validate max file count", () => {
|
|
157
|
+
const onValidationError = vi.fn();
|
|
158
|
+
const { state, onDrop } = useDragDrop({
|
|
159
|
+
maxFiles: 2,
|
|
160
|
+
onValidationError,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const files = [
|
|
164
|
+
createMockFile("file1.txt", 100, "text/plain"),
|
|
165
|
+
createMockFile("file2.txt", 100, "text/plain"),
|
|
166
|
+
createMockFile("file3.txt", 100, "text/plain"),
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
onDrop(createMockDragEvent("drop", files));
|
|
170
|
+
|
|
171
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
172
|
+
expect(state.value.errors.some((e) => e.includes("Maximum 2 files"))).toBe(
|
|
173
|
+
true,
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should validate max file size", () => {
|
|
178
|
+
const onValidationError = vi.fn();
|
|
179
|
+
const { state, onDrop } = useDragDrop({
|
|
180
|
+
maxFileSize: 1024, // 1KB
|
|
181
|
+
onValidationError,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const largeFile = createMockFile("large.txt", 2048, "text/plain"); // 2KB
|
|
185
|
+
|
|
186
|
+
onDrop(createMockDragEvent("drop", [largeFile]));
|
|
187
|
+
|
|
188
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
189
|
+
expect(
|
|
190
|
+
state.value.errors.some((e) => e.includes("exceeds maximum size")),
|
|
191
|
+
).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("should run custom validator", () => {
|
|
195
|
+
const onValidationError = vi.fn();
|
|
196
|
+
const customValidator = vi.fn((files: File[]) => {
|
|
197
|
+
if (files.some((f) => f.name.includes("bad"))) {
|
|
198
|
+
return ["File name contains 'bad'"];
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const { state, onDrop } = useDragDrop({
|
|
204
|
+
validator: customValidator,
|
|
205
|
+
onValidationError,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const badFile = createMockFile("bad-file.txt", 100, "text/plain");
|
|
209
|
+
onDrop(createMockDragEvent("drop", [badFile]));
|
|
210
|
+
|
|
211
|
+
expect(customValidator).toHaveBeenCalledWith([badFile]);
|
|
212
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
213
|
+
expect(state.value.errors).toContain("File name contains 'bad'");
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe("input change handling", () => {
|
|
218
|
+
it("should process files from input change", () => {
|
|
219
|
+
const onFilesReceived = vi.fn();
|
|
220
|
+
const { onInputChange } = useDragDrop({ onFilesReceived });
|
|
221
|
+
|
|
222
|
+
const file = createMockFile("test.txt", 100, "text/plain");
|
|
223
|
+
const event = createMockInputChangeEvent([file]);
|
|
224
|
+
|
|
225
|
+
onInputChange(event);
|
|
226
|
+
|
|
227
|
+
expect(onFilesReceived).toHaveBeenCalledWith([file]);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("should reset input value after processing", () => {
|
|
231
|
+
const { onInputChange } = useDragDrop();
|
|
232
|
+
|
|
233
|
+
const file = createMockFile("test.txt", 100, "text/plain");
|
|
234
|
+
const event = createMockInputChangeEvent([file]);
|
|
235
|
+
const input = event.target as HTMLInputElement;
|
|
236
|
+
|
|
237
|
+
onInputChange(event);
|
|
238
|
+
|
|
239
|
+
expect(input.value).toBe("");
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("inputProps", () => {
|
|
244
|
+
it("should provide correct input props", () => {
|
|
245
|
+
const { inputProps } = useDragDrop({
|
|
246
|
+
multiple: true,
|
|
247
|
+
accept: ["image/*", ".pdf"],
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(inputProps.value.type).toBe("file");
|
|
251
|
+
expect(inputProps.value.multiple).toBe(true);
|
|
252
|
+
expect(inputProps.value.accept).toBe("image/*, .pdf");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should default multiple to true", () => {
|
|
256
|
+
const { inputProps } = useDragDrop();
|
|
257
|
+
|
|
258
|
+
expect(inputProps.value.multiple).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("should handle undefined accept", () => {
|
|
262
|
+
const { inputProps } = useDragDrop();
|
|
263
|
+
|
|
264
|
+
expect(inputProps.value.accept).toBeUndefined();
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe("reset", () => {
|
|
269
|
+
it("should reset state to initial values", () => {
|
|
270
|
+
const onValidationError = vi.fn();
|
|
271
|
+
const { state, onDrop, reset } = useDragDrop({
|
|
272
|
+
accept: [".txt"],
|
|
273
|
+
onValidationError,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Trigger validation error
|
|
277
|
+
const invalidFile = createMockFile("test.pdf", 100, "application/pdf");
|
|
278
|
+
onDrop(createMockDragEvent("drop", [invalidFile]));
|
|
279
|
+
|
|
280
|
+
expect(state.value.isValid).toBe(false);
|
|
281
|
+
expect(state.value.errors.length).toBeGreaterThan(0);
|
|
282
|
+
|
|
283
|
+
// Reset
|
|
284
|
+
reset();
|
|
285
|
+
|
|
286
|
+
expect(state.value.isDragging).toBe(false);
|
|
287
|
+
expect(state.value.isOver).toBe(false);
|
|
288
|
+
expect(state.value.isValid).toBe(true);
|
|
289
|
+
expect(state.value.errors).toEqual([]);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe("processFiles", () => {
|
|
294
|
+
it("should allow direct file processing", () => {
|
|
295
|
+
const onFilesReceived = vi.fn();
|
|
296
|
+
const { processFiles } = useDragDrop({ onFilesReceived });
|
|
297
|
+
|
|
298
|
+
const files = [createMockFile("test.txt", 100, "text/plain")];
|
|
299
|
+
processFiles(files);
|
|
300
|
+
|
|
301
|
+
expect(onFilesReceived).toHaveBeenCalledWith(files);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
computed,
|
|
11
11
|
onMounted,
|
|
12
12
|
onUnmounted,
|
|
13
|
+
type Ref,
|
|
13
14
|
readonly,
|
|
14
15
|
ref,
|
|
15
16
|
shallowReadonly,
|
|
16
|
-
type Ref,
|
|
17
17
|
} from "vue";
|
|
18
18
|
import { useFlowManagerContext } from "./useFlowManagerContext";
|
|
19
19
|
import { useUploadistaClient } from "./useUploadistaClient";
|
|
@@ -306,7 +306,11 @@ export function useFlow(options: FlowUploadOptions): UseFlowReturn {
|
|
|
306
306
|
bytesAccepted: number,
|
|
307
307
|
bytesTotal: number | null,
|
|
308
308
|
) => {
|
|
309
|
-
optionsRef.value.onChunkComplete?.(
|
|
309
|
+
optionsRef.value.onChunkComplete?.(
|
|
310
|
+
chunkSize,
|
|
311
|
+
bytesAccepted,
|
|
312
|
+
bytesTotal,
|
|
313
|
+
);
|
|
310
314
|
},
|
|
311
315
|
onFlowComplete: (outputs: TypedOutput[]) => {
|
|
312
316
|
optionsRef.value.onFlowComplete?.(outputs);
|
|
@@ -167,10 +167,7 @@ export function useUploadEvents(options: UseUploadEventsOptions): void {
|
|
|
167
167
|
break;
|
|
168
168
|
case UploadEventType.UPLOAD_PROGRESS:
|
|
169
169
|
options.onUploadProgress?.({
|
|
170
|
-
...(event.data as unknown as Omit<
|
|
171
|
-
UploadProgressEventData,
|
|
172
|
-
"flow"
|
|
173
|
-
>),
|
|
170
|
+
...(event.data as unknown as Omit<UploadProgressEventData, "flow">),
|
|
174
171
|
flow: flowContext,
|
|
175
172
|
});
|
|
176
173
|
break;
|